-
-
Notifications
You must be signed in to change notification settings - Fork 44
feat: Add smoke tests for copi.owasp.org and cornucopia.owasp.org (fi… #2069
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds smoke tests for the copi.owasp.org and cornucopia.owasp.org applications to verify basic functionality and availability of deployed services, addressing issue #1265.
Changes:
- Added smoke_tests.py with test classes for both applications to verify route accessibility and JavaScript functionality
- Added comprehensive README documentation for running and understanding the smoke tests
- Created dedicated smoke-tests.yaml workflow for scheduled and on-demand testing
- Integrated smoke tests into the existing run-tests.yaml workflow
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
| tests/scripts/smoke_tests.py | Implements smoke test classes for copi.owasp.org, cornucopia.owasp.org, and integration tests between both applications |
| tests/scripts/README.md | Provides documentation on what the smoke tests check, how to run them, and their CI/CD integration |
| .github/workflows/smoke-tests.yaml | Creates a dedicated workflow for running smoke tests on a schedule, manual trigger, or when application code changes |
| .github/workflows/run-tests.yaml | Integrates smoke tests into the main test suite that runs on pull requests |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| - name: Run smoke tests | ||
| run: pipenv run python -m unittest tests.scripts.smoke_tests -v |
Copilot
AI
Jan 23, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The smoke tests added to this workflow will fail because the hardening job (line 8-10) blocks egress traffic to all endpoints except those explicitly allowed in hardening.yaml. The smoke tests need to access copi.owasp.org:443 and cornucopia.owasp.org:443, which are not in the allowed-endpoints list.
Either the hardening.yaml file needs to be updated to include these endpoints, or the smoke tests should be made conditional/skipped when hardening is enabled.
tests/scripts/README.md
Outdated
|
|
||
| ### For copi.owasp.org (Elixir/Phoenix) | ||
| 1. Homepage loads successfully (HTTP 200) | ||
| 2. Game route is accessible |
Copilot
AI
Jan 23, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The documentation states "Game route is accessible" but the corresponding test (test_02_game_route_accessible in smoke_tests.py) actually tests the homepage route ("/") again, not a game route. This is inconsistent with the implementation.
Either the documentation should be updated to reflect what is actually being tested, or the test should be fixed to test an actual game route like /cards or /games.
tests/scripts/smoke_tests.py
Outdated
| def test_01_homepage_loads(self) -> None: | ||
| """Test that the Copi homepage loads successfully""" | ||
| try: | ||
| response = requests.get(self.BASE_URL, timeout=30) | ||
| self.assertEqual(response.status_code, 200, f"Homepage returned status {response.status_code}") | ||
| self.assertIn("copi", response.text.lower(), "Homepage should contain 'copi' text") | ||
| except requests.exceptions.ConnectionError: | ||
| self.fail(f"Failed to connect to {self.BASE_URL} - service may be down") | ||
| except requests.exceptions.Timeout: | ||
| self.fail(f"Request to {self.BASE_URL} timed out after 30 seconds") | ||
|
|
||
| def test_02_game_route_accessible(self) -> None: | ||
| """Test that a game-related route is accessible""" | ||
| url = urljoin(self.BASE_URL, "/") | ||
| try: | ||
| response = requests.get(url, timeout=30) | ||
| self.assertEqual(response.status_code, 200, f"Game route returned status {response.status_code}") | ||
| except requests.exceptions.ConnectionError: | ||
| self.fail(f"Failed to connect to {url} - service may be down") | ||
| except requests.exceptions.Timeout: | ||
| self.fail(f"Request to {url} timed out after 30 seconds") | ||
|
|
||
| def test_03_javascript_loads(self) -> None: | ||
| """Test that JavaScript assets are being served""" | ||
| try: | ||
| response = requests.get(self.BASE_URL, timeout=30) | ||
| self.assertEqual(response.status_code, 200) | ||
| self.assertTrue( | ||
| '<script' in response.text or 'app.js' in response.text or 'phoenix' in response.text.lower(), | ||
| "JavaScript should be loaded on the page" | ||
| ) | ||
| except requests.exceptions.ConnectionError: | ||
| self.fail(f"Failed to connect to {self.BASE_URL} - service may be down") | ||
| except requests.exceptions.Timeout: | ||
| self.fail(f"Request to {self.BASE_URL} timed out after 30 seconds") | ||
|
|
||
| def test_04_health_check(self) -> None: | ||
| """Test that the application server is healthy and responding""" | ||
| try: | ||
| response = requests.get(self.BASE_URL, timeout=30) | ||
| self.assertEqual(response.status_code, 200) | ||
| self.assertIn('content-type', [h.lower() for h in response.headers.keys()], | ||
| "Response should include content-type header") | ||
| except requests.exceptions.ConnectionError: | ||
| self.fail(f"Failed to connect to {self.BASE_URL} - service may be down") | ||
| except requests.exceptions.Timeout: | ||
| self.fail(f"Request to {self.BASE_URL} timed out after 30 seconds") |
Copilot
AI
Jan 23, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The exception handling pattern (try-except blocks for ConnectionError and Timeout) is duplicated across all test methods in the class. This creates maintainability issues as any change to error handling needs to be replicated in multiple places.
Consider extracting this into a helper method that wraps the request logic, for example:
def _make_request(self, url: str, timeout: int = 30) -> requests.Response:
try:
return requests.get(url, timeout=timeout)
except requests.exceptions.ConnectionError:
self.fail(f"Failed to connect to {url} - service may be down")
except requests.exceptions.Timeout:
self.fail(f"Request to {url} timed out after {timeout} seconds")Then each test can simply call response = self._make_request(url) and focus on the assertions.
.github/workflows/smoke-tests.yaml
Outdated
| - name: Install Python dependencies | ||
| run: pip install -r requirements.txt --require-hashes | ||
|
|
||
| - name: Run smoke tests for copi.owasp.org | ||
| run: python -m unittest tests.scripts.smoke_tests.CopiSmokeTests -v | ||
| continue-on-error: false | ||
|
|
||
| - name: Run smoke tests for cornucopia.owasp.org | ||
| run: python -m unittest tests.scripts.smoke_tests.CornucopiaSmokeTests -v | ||
| continue-on-error: false | ||
|
|
||
| - name: Run integration smoke tests | ||
| run: python -m unittest tests.scripts.smoke_tests.IntegrationSmokeTests -v |
Copilot
AI
Jan 23, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The workflow installs dependencies using pip and requirements.txt, but the requests library (which is required by smoke_tests.py) is not in requirements.txt - it's only in the Pipfile. This will cause the smoke tests to fail with an import error.
The workflow should install dependencies using pipenv (similar to run-tests.yaml lines 26-29), or the requests library needs to be added to requirements.txt with its hash.
| - name: Install Python dependencies | |
| run: pip install -r requirements.txt --require-hashes | |
| - name: Run smoke tests for copi.owasp.org | |
| run: python -m unittest tests.scripts.smoke_tests.CopiSmokeTests -v | |
| continue-on-error: false | |
| - name: Run smoke tests for cornucopia.owasp.org | |
| run: python -m unittest tests.scripts.smoke_tests.CornucopiaSmokeTests -v | |
| continue-on-error: false | |
| - name: Run integration smoke tests | |
| run: python -m unittest tests.scripts.smoke_tests.IntegrationSmokeTests -v | |
| - name: Install Python dependencies with Pipenv | |
| run: | | |
| python -m pip install --upgrade pip | |
| python -m pip install pipenv | |
| pipenv install | |
| - name: Run smoke tests for copi.owasp.org | |
| run: pipenv run python -m unittest tests.scripts.smoke_tests.CopiSmokeTests -v | |
| continue-on-error: false | |
| - name: Run smoke tests for cornucopia.owasp.org | |
| run: pipenv run python -m unittest tests.scripts.smoke_tests.CornucopiaSmokeTests -v | |
| continue-on-error: false | |
| - name: Run integration smoke tests | |
| run: pipenv run python -m unittest tests.scripts.smoke_tests.IntegrationSmokeTests -v |
tests/scripts/smoke_tests.py
Outdated
| def test_02_game_route_accessible(self) -> None: | ||
| """Test that a game-related route is accessible""" | ||
| url = urljoin(self.BASE_URL, "/") | ||
| try: | ||
| response = requests.get(url, timeout=30) | ||
| self.assertEqual(response.status_code, 200, f"Game route returned status {response.status_code}") | ||
| except requests.exceptions.ConnectionError: | ||
| self.fail(f"Failed to connect to {url} - service may be down") | ||
| except requests.exceptions.Timeout: | ||
| self.fail(f"Request to {url} timed out after 30 seconds") |
Copilot
AI
Jan 23, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test_02_game_route_accessible method is testing the same route as test_01_homepage_loads. Using urljoin(self.BASE_URL, "/") simply returns the BASE_URL, so this test does not fulfill the requirement of testing "at least 2 routes" on the application.
Based on the copi.owasp.org router (lib/copi_web/router.ex), there are several actual routes available such as /cards or /games that could be tested instead. Consider changing this to test a different route, for example:
url = urljoin(self.BASE_URL, "/cards")
.github/workflows/smoke-tests.yaml
Outdated
| hardening: | ||
| name: Harden runner | ||
| uses: ./.github/workflows/hardening.yaml | ||
|
|
||
| smoke-tests: | ||
| name: Run Smoke Tests | ||
| needs: hardening |
Copilot
AI
Jan 23, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The smoke-tests workflow uses the hardening job which blocks egress traffic to all endpoints except those explicitly allowed. However, the smoke tests need to access copi.owasp.org:443 and cornucopia.owasp.org:443, which are not in the allowed-endpoints list in .github/workflows/hardening.yaml. This will cause the smoke tests to fail when they try to make HTTP requests to these domains.
The hardening.yaml file needs to be updated to include these endpoints in the allowed-endpoints list, or the smoke-tests workflow should not use the hardening job.
| hardening: | |
| name: Harden runner | |
| uses: ./.github/workflows/hardening.yaml | |
| smoke-tests: | |
| name: Run Smoke Tests | |
| needs: hardening | |
| smoke-tests: | |
| name: Run Smoke Tests |
…v, fix duplicate route, add helper methods
|
Sorry I havent' been able to get back to you sooner. Thank you for your effort. |
|
@sydseter Thanks for the feedback. I’ll update this PR so the smoke tests run only against the Dockerized copi app and database in the CI pipeline (localhost) and not production or stage, and I’ll remove the cornucopia.owasp.org smoke tests because Vite’s checks already validate those routes |
fixes #1265)