From d9114c900db5e2ab6fd305145f364baacc774829 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josef=20=C5=A0im=C3=A1nek?= Date: Sun, 2 Nov 2025 13:51:29 +0100 Subject: [PATCH] API v1 initial tests. --- .github/workflows/ci.yaml | 50 ++++++ pgcommitfest/commitfest/tests/__init__.py | 1 + pgcommitfest/commitfest/tests/test_apiv1.py | 159 ++++++++++++++++++++ 3 files changed, 210 insertions(+) create mode 100644 pgcommitfest/commitfest/tests/__init__.py create mode 100644 pgcommitfest/commitfest/tests/test_apiv1.py diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 2beb0666..2fb90774 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -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 diff --git a/pgcommitfest/commitfest/tests/__init__.py b/pgcommitfest/commitfest/tests/__init__.py new file mode 100644 index 00000000..7d5a9d90 --- /dev/null +++ b/pgcommitfest/commitfest/tests/__init__.py @@ -0,0 +1 @@ +# Tests for the commitfest application diff --git a/pgcommitfest/commitfest/tests/test_apiv1.py b/pgcommitfest/commitfest/tests/test_apiv1.py new file mode 100644 index 00000000..19633386 --- /dev/null +++ b/pgcommitfest/commitfest/tests/test_apiv1.py @@ -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)