Skip to content

Commit 9433198

Browse files
author
Yasser Toruno
committed
add page specific tests and github actions setup for testing with celery
1 parent 40be84c commit 9433198

8 files changed

Lines changed: 443 additions & 2 deletions

File tree

.github/workflows/test.yml

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,16 +45,54 @@ jobs:
4545
make install
4646
pip install "Django~=${{ matrix.django-version }}.0"
4747
48+
- name: Run database migrations
49+
run: |
50+
cd example_project
51+
python manage.py migrate --noinput
52+
env:
53+
DB_ENGINE: postgresql
54+
POSTGRES_HOST: localhost
55+
POSTGRES_PORT: 5432
56+
POSTGRES_USER: postgres
57+
POSTGRES_PASSWORD: postgres
58+
POSTGRES_DB: postgres
59+
CELERY_BROKER_URL: redis://localhost:6379/0
60+
61+
- name: Start Celery worker
62+
run: |
63+
cd example_project
64+
celery -A example_project worker --loglevel=info --detach --logfile=celery_worker.log
65+
env:
66+
DB_ENGINE: postgresql
67+
POSTGRES_HOST: localhost
68+
POSTGRES_PORT: 5432
69+
POSTGRES_USER: postgres
70+
POSTGRES_PASSWORD: postgres
71+
POSTGRES_DB: postgres
72+
CELERY_BROKER_URL: redis://localhost:6379/0
73+
74+
- name: Wait for worker to be ready
75+
run: |
76+
echo "Waiting for Celery worker to register..."
77+
sleep 5
78+
4879
- name: Run tests with coverage
4980
run: |
50-
pytest --cov=dj_cache_panel --cov-report=xml
81+
pytest --cov=dj_celery_panel --cov-report=xml
5182
env:
5283
TEST_DB_BACKEND: postgresql
5384
POSTGRES_HOST: localhost
5485
POSTGRES_PORT: 5432
5586
POSTGRES_USER: postgres
5687
POSTGRES_PASSWORD: postgres
5788
POSTGRES_DB: postgres
89+
CELERY_BROKER_URL: redis://localhost:6379/0
90+
91+
- name: Show Celery worker logs on failure
92+
if: failure()
93+
run: |
94+
echo "=== Celery Worker Logs ==="
95+
cat example_project/celery_worker.log || echo "No worker log file found"
5896
5997
- name: Upload coverage to Codecov
6098
uses: codecov/codecov-action@v5

example_project/example_project/settings.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,8 @@
166166
# Celery Configuration #
167167
#########################
168168
# Celery Configuration (using modern Celery 4.x+ naming)
169-
CELERY_BROKER_URL = "redis://redis:6379/0"
169+
# Use environment variable for broker URL (defaults to Docker Compose hostname)
170+
CELERY_BROKER_URL = os.environ.get("CELERY_BROKER_URL", "redis://redis:6379/0")
170171

171172
CELERY_BROKER_TRANSPORT_OPTIONS = {
172173
"visibility_timeout": 3600,

tests/conftest.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ def pytest_configure(config):
3535
else:
3636
os.environ.setdefault("DB_ENGINE", "sqlite")
3737

38+
# Set Celery broker URL (defaults to localhost for tests)
39+
os.environ.setdefault("CELERY_BROKER_URL", "redis://localhost:6379/0")
40+
3841
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "example_project.settings")
3942

4043
if not settings.configured:

tests/test_configuration.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
"""
2+
Tests for the configuration page.
3+
"""
4+
5+
from django.urls import reverse
6+
7+
from .base import CeleryPanelTestCase
8+
9+
10+
class TestConfigurationPage(CeleryPanelTestCase):
11+
"""Test cases for the configuration page."""
12+
13+
def test_configuration_page_loads(self):
14+
"""Test that the configuration page loads successfully."""
15+
response = self.client.get(reverse("dj_celery_panel:configuration"))
16+
17+
self.assertEqual(response.status_code, 200)
18+
19+
def test_configuration_shows_panel_settings(self):
20+
"""Test that the configuration page shows DJ Celery Panel settings."""
21+
response = self.client.get(reverse("dj_celery_panel:configuration"))
22+
23+
self.assertEqual(response.status_code, 200)
24+
self.assertContains(response, "DJ Celery Panel Settings")
25+
self.assertContains(response, "Backend Configuration")
26+
27+
def test_configuration_shows_celery_settings(self):
28+
"""Test that the configuration page shows Celery settings."""
29+
response = self.client.get(reverse("dj_celery_panel:configuration"))
30+
31+
self.assertEqual(response.status_code, 200)
32+
self.assertContains(response, "Celery Settings")
33+
self.assertContains(response, "Connection & Serialization")
34+
35+
def test_configuration_shows_task_execution_settings(self):
36+
"""Test that the configuration page shows task execution settings."""
37+
response = self.client.get(reverse("dj_celery_panel:configuration"))
38+
39+
self.assertEqual(response.status_code, 200)
40+
self.assertContains(response, "Task Execution")
41+
42+
def test_configuration_shows_queue_routing_settings(self):
43+
"""Test that the configuration page shows queue and routing settings."""
44+
response = self.client.get(reverse("dj_celery_panel:configuration"))
45+
46+
self.assertEqual(response.status_code, 200)
47+
self.assertContains(response, "Queue & Routing")
48+
49+
def test_configuration_shows_worker_settings(self):
50+
"""Test that the configuration page shows worker settings."""
51+
response = self.client.get(reverse("dj_celery_panel:configuration"))
52+
53+
self.assertEqual(response.status_code, 200)
54+
self.assertContains(response, "Worker Settings")
55+
56+
def test_configuration_requires_authentication(self):
57+
"""Test that unauthenticated users cannot access the configuration page."""
58+
from django.test import Client
59+
60+
client = Client()
61+
response = client.get(reverse("dj_celery_panel:configuration"))
62+
63+
# Should redirect to login
64+
self.assertEqual(response.status_code, 302)
65+
66+
def test_configuration_requires_staff_permission(self):
67+
"""Test that non-staff users cannot access the configuration page."""
68+
from django.test import Client
69+
from django.contrib.auth import get_user_model
70+
71+
User = get_user_model()
72+
user = User.objects.create_user(
73+
username="regular_user", password="testpass123", is_staff=False
74+
)
75+
76+
client = Client()
77+
client.force_login(user)
78+
response = client.get(reverse("dj_celery_panel:configuration"))
79+
80+
# Should redirect to admin login
81+
self.assertEqual(response.status_code, 302)

tests/test_index.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
"""
2+
Tests for the Celery Panel index/overview page.
3+
"""
4+
5+
from django.urls import reverse
6+
7+
from .base import CeleryPanelTestCase
8+
9+
10+
class TestIndexPage(CeleryPanelTestCase):
11+
"""Test cases for the index page."""
12+
13+
def test_index_page_loads(self):
14+
"""Test that the index page loads successfully."""
15+
response = self.client.get(reverse("dj_celery_panel:index"))
16+
17+
self.assertEqual(response.status_code, 200)
18+
self.assertContains(response, "Django Celery Panel")
19+
20+
def test_index_shows_configuration_section(self):
21+
"""Test that the index page shows configuration information."""
22+
response = self.client.get(reverse("dj_celery_panel:index"))
23+
24+
self.assertEqual(response.status_code, 200)
25+
# Should show broker and result backend info
26+
self.assertContains(response, "Broker")
27+
self.assertContains(response, "Result Backend")
28+
29+
def test_index_shows_registered_tasks(self):
30+
"""Test that the index page shows registered tasks."""
31+
response = self.client.get(reverse("dj_celery_panel:index"))
32+
33+
self.assertEqual(response.status_code, 200)
34+
self.assertContains(response, "Registered Tasks")
35+
36+
def test_index_shows_periodic_tasks(self):
37+
"""Test that the index page shows periodic tasks."""
38+
response = self.client.get(reverse("dj_celery_panel:index"))
39+
40+
self.assertEqual(response.status_code, 200)
41+
self.assertContains(response, "Periodic Tasks")
42+
43+
def test_index_requires_authentication(self):
44+
"""Test that unauthenticated users cannot access the index page."""
45+
from django.test import Client
46+
47+
client = Client()
48+
response = client.get(reverse("dj_celery_panel:index"))
49+
50+
# Should redirect to login
51+
self.assertEqual(response.status_code, 302)
52+
53+
def test_index_requires_staff_permission(self):
54+
"""Test that non-staff users cannot access the index page."""
55+
from django.test import Client
56+
from django.contrib.auth import get_user_model
57+
58+
User = get_user_model()
59+
user = User.objects.create_user(
60+
username="regular_user", password="testpass123", is_staff=False
61+
)
62+
63+
client = Client()
64+
client.force_login(user)
65+
response = client.get(reverse("dj_celery_panel:index"))
66+
67+
# Should redirect to admin login
68+
self.assertEqual(response.status_code, 302)

tests/test_queues.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
"""
2+
Tests for the queues page and queue detail page.
3+
"""
4+
5+
from django.urls import reverse
6+
7+
from .base import CeleryPanelTestCase
8+
9+
10+
class TestQueuesPage(CeleryPanelTestCase):
11+
"""Test cases for the queues list page."""
12+
13+
def test_queues_page_loads(self):
14+
"""Test that the queues page loads successfully."""
15+
response = self.client.get(reverse("dj_celery_panel:queues"))
16+
17+
self.assertEqual(response.status_code, 200)
18+
self.assertContains(response, "Active Task Queues")
19+
20+
def test_queues_page_shows_table_headers(self):
21+
"""Test that the queues page shows appropriate table headers."""
22+
response = self.client.get(reverse("dj_celery_panel:queues"))
23+
24+
self.assertEqual(response.status_code, 200)
25+
self.assertContains(response, "Queue Name")
26+
self.assertContains(response, "Messages")
27+
self.assertContains(response, "Exchange")
28+
self.assertContains(response, "Routing Key")
29+
30+
def test_queues_page_handles_no_queues(self):
31+
"""Test that the queues page handles case when no queues are active."""
32+
response = self.client.get(reverse("dj_celery_panel:queues"))
33+
34+
# Should still load successfully even with no queues
35+
self.assertEqual(response.status_code, 200)
36+
37+
def test_queues_requires_authentication(self):
38+
"""Test that unauthenticated users cannot access the queues page."""
39+
from django.test import Client
40+
41+
client = Client()
42+
response = client.get(reverse("dj_celery_panel:queues"))
43+
44+
# Should redirect to login
45+
self.assertEqual(response.status_code, 302)
46+
47+
48+
class TestQueueDetailPage(CeleryPanelTestCase):
49+
"""Test cases for the queue detail page."""
50+
51+
def test_queue_detail_page_loads_with_valid_queue_name(self):
52+
"""Test that the queue detail page loads with a queue name."""
53+
# Using a dummy queue name - the page should load even if queue doesn't exist
54+
response = self.client.get(
55+
reverse("dj_celery_panel:queue_detail", kwargs={"queue_name": "celery"})
56+
)
57+
58+
self.assertEqual(response.status_code, 200)
59+
self.assertContains(response, "Queue Details")
60+
61+
def test_queue_detail_shows_not_found_for_invalid_queue(self):
62+
"""Test that the queue detail page shows error for invalid queue."""
63+
response = self.client.get(
64+
reverse("dj_celery_panel:queue_detail", kwargs={"queue_name": "nonexistent-queue"})
65+
)
66+
67+
self.assertEqual(response.status_code, 200)
68+
# Should show some indication that queue wasn't found
69+
70+
def test_queue_detail_requires_authentication(self):
71+
"""Test that unauthenticated users cannot access queue detail."""
72+
from django.test import Client
73+
74+
client = Client()
75+
response = client.get(
76+
reverse("dj_celery_panel:queue_detail", kwargs={"queue_name": "celery"})
77+
)
78+
79+
# Should redirect to login
80+
self.assertEqual(response.status_code, 302)

tests/test_tasks.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
"""
2+
Tests for the tasks page and task detail page.
3+
"""
4+
5+
from django.urls import reverse
6+
7+
from .base import CeleryPanelTestCase
8+
9+
10+
class TestTasksPage(CeleryPanelTestCase):
11+
"""Test cases for the tasks list page."""
12+
13+
def test_tasks_page_loads(self):
14+
"""Test that the tasks page loads successfully."""
15+
response = self.client.get(reverse("dj_celery_panel:tasks"))
16+
17+
self.assertEqual(response.status_code, 200)
18+
self.assertContains(response, "Task Execution History")
19+
20+
def test_tasks_page_shows_search_bar(self):
21+
"""Test that the tasks page has a search bar."""
22+
response = self.client.get(reverse("dj_celery_panel:tasks"))
23+
24+
self.assertEqual(response.status_code, 200)
25+
self.assertContains(response, 'name="search"')
26+
27+
def test_tasks_page_with_search_query(self):
28+
"""Test that the tasks page accepts search queries."""
29+
response = self.client.get(reverse("dj_celery_panel:tasks"), {"search": "test_task"})
30+
31+
self.assertEqual(response.status_code, 200)
32+
self.assertContains(response, 'value="test_task"')
33+
34+
def test_tasks_page_pagination(self):
35+
"""Test that the tasks page handles pagination."""
36+
response = self.client.get(reverse("dj_celery_panel:tasks"), {"page": "1"})
37+
38+
self.assertEqual(response.status_code, 200)
39+
40+
def test_tasks_page_invalid_page_number(self):
41+
"""Test that the tasks page handles invalid page numbers gracefully."""
42+
response = self.client.get(reverse("dj_celery_panel:tasks"), {"page": "invalid"})
43+
44+
# Should default to page 1
45+
self.assertEqual(response.status_code, 200)
46+
47+
def test_tasks_requires_authentication(self):
48+
"""Test that unauthenticated users cannot access the tasks page."""
49+
from django.test import Client
50+
51+
client = Client()
52+
response = client.get(reverse("dj_celery_panel:tasks"))
53+
54+
# Should redirect to login
55+
self.assertEqual(response.status_code, 302)
56+
57+
58+
class TestTaskDetailPage(CeleryPanelTestCase):
59+
"""Test cases for the task detail page."""
60+
61+
def test_task_detail_page_loads_with_valid_task_id(self):
62+
"""Test that the task detail page loads with a task ID."""
63+
# Using a dummy task ID - the page should load even if task doesn't exist
64+
response = self.client.get(
65+
reverse("dj_celery_panel:task_detail", kwargs={"task_id": "test-task-id"})
66+
)
67+
68+
self.assertEqual(response.status_code, 200)
69+
70+
def test_task_detail_shows_not_found_for_invalid_task(self):
71+
"""Test that the task detail page shows error for invalid task."""
72+
response = self.client.get(
73+
reverse("dj_celery_panel:task_detail", kwargs={"task_id": "nonexistent-task"})
74+
)
75+
76+
self.assertEqual(response.status_code, 200)
77+
# Should show some indication that task wasn't found
78+
# Either in messages or in the page content
79+
80+
def test_task_detail_requires_authentication(self):
81+
"""Test that unauthenticated users cannot access task detail."""
82+
from django.test import Client
83+
84+
client = Client()
85+
response = client.get(
86+
reverse("dj_celery_panel:task_detail", kwargs={"task_id": "test-task-id"})
87+
)
88+
89+
# Should redirect to login
90+
self.assertEqual(response.status_code, 302)

0 commit comments

Comments
 (0)