Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,53 @@ jobs:

- name: Run djhtml
run: djhtml pgcommitfest/*/templates/*.html pgcommitfest/*/templates/*.inc --tabwidth=1 --check

test:
runs-on: ubuntu-24.04
name: "Django Tests"

services:
postgres:
image: postgres:14
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Python with uv
uses: astral-sh/setup-uv@v4

- name: Create CI settings
run: |
cat > pgcommitfest/local_settings.py << 'EOF'
# CI test settings
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql_psycopg2",
"NAME": "pgcommitfest",
"USER": "postgres",
"PASSWORD": "postgres",
"HOST": "localhost",
"PORT": "5432",
}
}

# Enable debug for better error messages in CI
DEBUG = True
EOF

- name: Install dependencies
run: uv sync

- name: Run tests
run: uv run manage.py test --verbosity=2
1 change: 1 addition & 0 deletions pgcommitfest/commitfest/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Tests for the commitfest application
159 changes: 159 additions & 0 deletions pgcommitfest/commitfest/tests/test_apiv1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
from django.test import Client, TestCase, override_settings

import json
from datetime import date, timedelta

from pgcommitfest.commitfest.models import CommitFest


@override_settings(AUTO_CREATE_COMMITFESTS=False)
class NeedsCIEndpointTestCase(TestCase):
"""Test the /api/v1/commitfests/needs_ci endpoint."""

@classmethod
def setUpTestData(cls):
today = date.today()

# Create test commitfests with various statuses
cls.open_cf = CommitFest.objects.create(
name="2025-01",
status=CommitFest.STATUS_OPEN,
startdate=today - timedelta(days=30),
enddate=today + timedelta(days=30),
draft=False,
)

cls.in_progress_cf = CommitFest.objects.create(
name="2024-11",
status=CommitFest.STATUS_INPROGRESS,
startdate=today - timedelta(days=60),
enddate=today + timedelta(days=0),
draft=False,
)

# Previous CF that ended 3 days ago (should be included - within 7 day window)
cls.recent_previous_cf = CommitFest.objects.create(
name="2024-09",
status=CommitFest.STATUS_CLOSED,
startdate=today - timedelta(days=90),
enddate=today - timedelta(days=3),
draft=False,
)

# Old previous CF that ended 10 days ago (should be excluded - outside 7 day window)
cls.old_previous_cf = CommitFest.objects.create(
name="2024-07",
status=CommitFest.STATUS_CLOSED,
startdate=today - timedelta(days=120),
enddate=today - timedelta(days=10),
draft=False,
)

# Draft commitfest
cls.draft_cf = CommitFest.objects.create(
name="2025-03-draft",
status=CommitFest.STATUS_OPEN,
startdate=today + timedelta(days=60),
enddate=today + timedelta(days=120),
draft=True,
)

def setUp(self):
self.client = Client()

def test_endpoint_returns_200(self):
"""Test that the endpoint returns HTTP 200 OK."""
response = self.client.get("/api/v1/commitfests/needs_ci")
self.assertEqual(response.status_code, 200)

def test_response_is_valid_json(self):
"""Test that the response is valid JSON."""
response = self.client.get("/api/v1/commitfests/needs_ci")
try:
data = json.loads(response.content)
except json.JSONDecodeError:
self.fail("Response is not valid JSON")

self.assertIn("commitfests", data)
self.assertIsInstance(data["commitfests"], dict)

def test_response_content_type(self):
"""Test that the response has correct Content-Type header."""
response = self.client.get("/api/v1/commitfests/needs_ci")
self.assertEqual(response["Content-Type"], "application/json")

def test_cors_header_present(self):
"""Test that CORS header is present for API access."""
response = self.client.get("/api/v1/commitfests/needs_ci")
self.assertEqual(response["Access-Control-Allow-Origin"], "*")

def test_includes_open_commitfest(self):
"""Test that open commitfests are included in response."""
response = self.client.get("/api/v1/commitfests/needs_ci")
data = json.loads(response.content)
commitfests = data["commitfests"]

# Should include the open commitfest
self.assertIn("open", commitfests)
self.assertEqual(commitfests["open"]["name"], self.open_cf.name)

def test_includes_in_progress_commitfest(self):
"""Test that in-progress commitfests are included in response."""
response = self.client.get("/api/v1/commitfests/needs_ci")
data = json.loads(response.content)
commitfests = data["commitfests"]

# Should include the in-progress commitfest
self.assertEqual(commitfests["in_progress"]["name"], self.in_progress_cf.name)

def test_includes_recent_previous_commitfest(self):
"""Test that recently ended commitfests are included (within 7 days)."""
response = self.client.get("/api/v1/commitfests/needs_ci")
data = json.loads(response.content)
commitfests = data["commitfests"]

# Should include recent previous commitfest (ended 3 days ago)
self.assertIsNotNone(commitfests["previous"])

def test_excludes_old_previous_commitfest(self):
"""Test that old commitfests are excluded (older than 7 days)."""
response = self.client.get("/api/v1/commitfests/needs_ci")
data = json.loads(response.content)
commitfests = data["commitfests"]

# Should not include old previous commitfest (ended 10 days ago)
self.assertNotEqual(
commitfests["previous"]["name"],
self.old_previous_cf.name,
"Old previous commitfest should be excluded",
)

def test_excludes_next_open_and_final(self):
"""Test that next_open and final are excluded from response."""
response = self.client.get("/api/v1/commitfests/needs_ci")
data = json.loads(response.content)
commitfests = data["commitfests"]

# These keys should not be present in the response
self.assertNotIn("next_open", commitfests)
self.assertNotIn("final", commitfests)

def test_response_structure(self):
"""Test that response has expected structure."""
response = self.client.get("/api/v1/commitfests/needs_ci")
data = json.loads(response.content)

# Top-level structure
self.assertIn("commitfests", data)
self.assertIsInstance(data["commitfests"], dict)

# Check that commitfest objects have expected fields
commitfests = data["commitfests"]
for key, cf_data in commitfests.items():
self.assertIsInstance(cf_data, dict)
# Basic fields that should be present
self.assertIn("id", cf_data)
self.assertIn("name", cf_data)
self.assertIn("status", cf_data)
self.assertIn("startdate", cf_data)
self.assertIn("enddate", cf_data)