Skip to content

Commit 0d978d7

Browse files
authored
Merge branch 'dev' into 1177-notifications-update-slack-notification-pipeline
2 parents 9263034 + 9318ab4 commit 0d978d7

File tree

10 files changed

+244
-33
lines changed

10 files changed

+244
-33
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name: Django Test Suite on PR
2+
3+
on:
4+
pull_request:
5+
branches:
6+
- dev
7+
8+
jobs:
9+
run-tests:
10+
runs-on: ubuntu-latest
11+
12+
services:
13+
docker:
14+
image: docker:24.0.5
15+
options: --privileged
16+
ports:
17+
- 5432:5432
18+
19+
steps:
20+
- name: Check out merged code
21+
uses: actions/checkout@v2
22+
23+
- name: Set up Docker Compose
24+
run: |
25+
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
26+
sudo chmod +x /usr/local/bin/docker-compose
27+
28+
- name: Build the Docker environment
29+
run: docker-compose -f local.yml build
30+
31+
- name: Run test suite
32+
env:
33+
DJANGO_ENV: test
34+
run: docker-compose -f local.yml run --rm django bash ./init.sh
35+
36+
- name: Cleanup
37+
run: docker-compose -f local.yml down --volumes

.pre-commit-config.yaml

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ repos:
88
- id: trailing-whitespace
99
- id: end-of-file-fixer
1010
- id: check-yaml
11+
- id: check-merge-conflict
12+
- id: debug-statements
1113

1214
- repo: https://github.com/asottile/pyupgrade
1315
rev: v3.17.0
@@ -37,14 +39,41 @@ repos:
3739
hooks:
3840
- id: mypy
3941
args: ["--strict"]
40-
# ignoring everything for now
41-
exclude: .
42-
additional_dependencies: [django-stubs, celery, django-environ, django-extensions, django-crispy-forms,
43-
crispy-bootstrap5, django-allauth, django-celery-beat, djangorestframework, djangorestframework-datatables,
44-
django-debug-toolbar, psycopg2-binary, python-slugify, xmltodict, PyGithub, boto3, scrapy, types-requests]
42+
exclude: "."
43+
additional_dependencies:
44+
- django-stubs
45+
- celery
46+
- django-environ
47+
- django-extensions
48+
- django-crispy-forms
49+
- crispy-bootstrap5
50+
- django-allauth
51+
- django-celery-beat
52+
- djangorestframework
53+
- djangorestframework-datatables
54+
- django-debug-toolbar
55+
- psycopg2-binary
56+
- python-slugify
57+
- xmltodict
58+
- PyGithub
59+
- boto3
60+
- scrapy
61+
- types-requests
62+
63+
- repo: https://github.com/PyCQA/bandit
64+
rev: '1.7.0'
65+
hooks:
66+
- id: bandit
67+
args: ['-r', '--configfile=bandit-config.yml']
68+
69+
- repo: https://github.com/zricethezav/gitleaks
70+
rev: 'v8.0.4'
71+
hooks:
72+
- id: gitleaks
73+
args: ['--config=gitleaks-config.toml']
74+
4575

4676

47-
# sets up .pre-commit-ci.yaml to ensure pre-commit dependencies stay up to date
4877
ci:
4978
autoupdate_schedule: weekly
5079
skip: []

CODE_STANDARDS.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Coding Standards and Conventions for COSMOS
2+
3+
## Overview
4+
To maintain high-quality code and ensure consistency across the entire COSMOS project, we have established coding standards and conventions. This document outlines the key standards and practices that all contributors are expected to follow. Adhering to these guidelines helps us to achieve a codebase that appears as if it were written by a single entity, regardless of the number of contributors.
5+
6+
## Coding Standards
7+
8+
### Formatting Standards
9+
- **Line Length**: Maximum of 120 characters per line to ensure readability across various environments.
10+
- **Code Formatting**: Utilize tools like Black for Python code to ensure consistent formatting across the entire codebase.
11+
- **Import Ordering**: Follow a consistent import order:
12+
- Standard library imports.
13+
- Third-party imports.
14+
- Application-specific imports.
15+
16+
### Naming Conventions
17+
- **Variables and Functions**: Use `snake_case`.
18+
- **Classes and Exceptions**: Use `CamelCase`.
19+
- **Constants**: Use `UPPER_CASE`.
20+
21+
### Commenting
22+
- Inline comments should be used sparingly and only when necessary to explain "why" something is done, not "what" is done.
23+
- All public methods, classes, and modules should include docstrings that follow the [Google style guide](https://google.github.io/styleguide/pyguide.html).
24+
25+
### Error Handling
26+
- Explicit is better than implicit. Raise exceptions rather than returning None or any error codes.
27+
- Use custom exceptions over generic exceptions when possible to make error handling more predictive.
28+
29+
## Tool Configurations and Pre-commit Hooks
30+
31+
To automate and enforce these standards, the following tools are configured with pre-commit hooks in our development process:
32+
33+
### Pre-commit Hooks Setup
34+
35+
To ensure that these tools are run automatically on every commit, contributors must set up pre-commit hooks locally. Run the following commands to install and configure pre-commit hooks:
36+
37+
```bash
38+
pip install pre-commit
39+
pre-commit install
40+
pre-commit run --all-files
41+
```
42+
43+
The following pre-commit hooks are configured:
44+
45+
- trailing-whitespace, end-of-file-fixer, check-yaml, check-merge-conflict, debug-statements: Checks for common formatting issues.
46+
- pyupgrade: Automatically upgrades syntax for newer versions of the language.
47+
- black: Formats Python code to ensure consistent styling.
48+
- isort: Sorts imports alphabetically and automatically separated into sections.
49+
- flake8: Lints code to catch styling errors and potential bugs.
50+
- mypy: Checks type annotations to catch potential bugs.
51+
- bandit: Scans code for common security issues.
52+
- gitleaks: Prevents secrets from being committed to the repository.
53+
- hadolint: Lints Dockerfiles to ensure best practices and common conventions are followed.
54+
55+
## Continuous Integration (CI)
56+
When a commit is pushed to a branch that is part of a Pull Request, our Continuous Integration (CI) pipeline automatically runs specified tools to check code quality, style, security and other standards. If these checks fail, the PR cannot be merged until all issues are resolved.
57+
58+
## Quality Standards Enforcement
59+
- PRs must pass all checks from the configured pre-commit hooks and CI pipeline to be eligible for merging.
60+
- Code reviews additionally focus on logical errors and code quality beyond what automated tools can detect.
61+
62+
## Conclusion
63+
By adhering to these standards and utilizing the tools set up, we maintain the high quality and consistency of our codebase, making it easier for developers to collaborate effectively.

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ $ pip install pre-commit
225225
$ pre-commit install
226226
$ pre-commit run --all-files
227227
```
228+
For detailed information on the coding standards and conventions we enforce, please see our [Coding Standards and Conventions](CODE_STANDARDS.md).
228229

229230
### Sentry Setup
230231

bandit-config.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# bandit-config.yml
2+
skips:
3+
- B101 # Skip assert used (often used in tests)
4+
- B403 # Skip import from the pickle module
5+
6+
exclude:
7+
- ./tests/ # Exclude test directories
8+
- ./migrations/ # Exclude migration directories
9+
- ./venv/ # Exclude virtual environment
10+
11+
tests:
12+
- B105 # Include test for hardcoded password strings
13+
- B602 # Include test for subprocess call with shell equals true
14+
15+
profiles:
16+
default:
17+
include:
18+
- B403 # Include test for dangerous default argument
19+
exclude:
20+
- B401 # Exclude test for import telnetlib
21+
22+
# Set the severity level to focus on higher-risk issues
23+
severity: 'HIGH'
24+
25+
# Set the confidence level to ensure that reported issues are likely true positives
26+
confidence: 'HIGH'
File renamed without changes.

init.sh

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#!/bin/bash
2+
echo "Running all test cases across the project..."
3+
4+
# Initialize a failure counter
5+
failure_count=0
6+
7+
# Exclude tests in `document_classifier` and `functional_tests` directories
8+
excluded_dirs="document_classifier functional_tests"
9+
10+
# Find all test files except those in excluded directories
11+
test_files=$(find . -type f -name "test_*.py" | grep -Ev "$(echo $excluded_dirs | sed 's/ /|/g')")
12+
13+
# Run each test file
14+
for test_file in $test_files; do
15+
echo "Running $test_file..."
16+
pytest "$test_file"
17+
18+
# Check the exit status of pytest
19+
if [ $? -ne 0 ]; then
20+
echo "Test failed: $test_file"
21+
failure_count=$((failure_count + 1))
22+
fi
23+
done
24+
25+
# Report the results
26+
if [ $failure_count -ne 0 ]; then
27+
echo "$failure_count test(s) failed."
28+
exit 1
29+
else
30+
echo "All tests passed successfully!"
31+
fi

sde_collections/tests/test_models_collections.py

Lines changed: 0 additions & 12 deletions
This file was deleted.

sde_collections/tests/test_workflow_status_triggers.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,20 @@ def test_quality_check_perfect_triggers_public_query(self, mock_add):
6363

6464
class TestReindexingStatusTransitions(TestCase):
6565
def setUp(self):
66+
# Mock the GitHubHandler to return valid XML content
67+
self.mock_github_handler = patch("sde_collections.models.collection.GitHubHandler").start()
68+
69+
self.mock_github_handler.return_value._get_file_contents.return_value.decoded_content = (
70+
b'<?xml version="1.0" encoding="UTF-8"?>\n'
71+
b"<Sinequa>\n"
72+
b" <KeepHashFragmentInUrl>false</KeepHashFragmentInUrl>\n"
73+
b" <CollectionSelection>Sample Collection</CollectionSelection>\n"
74+
b"</Sinequa>"
75+
)
76+
77+
self.addCleanup(patch.stopall)
78+
79+
# Create the collection with the mock applied
6680
self.collection = CollectionFactory(
6781
workflow_status=WorkflowStatusChoices.QUALITY_CHECK_PERFECT,
6882
reindexing_status=ReindexingStatusChoices.REINDEXING_NOT_NEEDED,

sde_indexing_helper/users/tests/test_views.py

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010

1111
from sde_indexing_helper.users.forms import UserAdminChangeForm
1212
from sde_indexing_helper.users.models import User
13-
from sde_indexing_helper.users.tests.factories import UserFactory
1413
from sde_indexing_helper.users.views import (
1514
UserRedirectView,
1615
UserUpdateView,
@@ -22,39 +21,45 @@
2221

2322
class TestUserUpdateView:
2423
"""
25-
TODO:
26-
extracting view initialization code as class-scoped fixture
27-
would be great if only pytest-django supported non-function-scoped
28-
fixture db access -- this is a work-in-progress for now:
29-
https://github.com/pytest-dev/pytest-django/pull/258
24+
Tests for the UserUpdateView.
3025
"""
3126

32-
def dummy_get_response(self, request: HttpRequest):
27+
@staticmethod
28+
def dummy_get_response(request: HttpRequest):
29+
"""Dummy get_response method for middleware testing."""
3330
return None
3431

3532
def test_get_success_url(self, user: User, rf: RequestFactory):
33+
"""
34+
Test that UserUpdateView redirects to the correct success URL.
35+
"""
3636
view = UserUpdateView()
3737
request = rf.get("/fake-url/")
3838
request.user = user
39-
4039
view.request = request
4140

42-
assert view.get_success_url() == f"/users/{user.username}/"
41+
expected_url = f"/users/{user.username}/"
42+
assert view.get_success_url() == expected_url, f"Expected {expected_url}, got {view.get_success_url()}"
4343

4444
def test_get_object(self, user: User, rf: RequestFactory):
45+
"""
46+
Test that UserUpdateView retrieves the correct user object.
47+
"""
4548
view = UserUpdateView()
4649
request = rf.get("/fake-url/")
4750
request.user = user
48-
4951
view.request = request
5052

5153
assert view.get_object() == user
5254

5355
def test_form_valid(self, user: User, rf: RequestFactory):
56+
"""
57+
Test that form submission in UserUpdateView processes correctly.
58+
"""
5459
view = UserUpdateView()
5560
request = rf.get("/fake-url/")
5661

57-
# Add the session/message middleware to the request
62+
# Add session and message middleware
5863
SessionMiddleware(self.dummy_get_response).process_request(request)
5964
MessageMiddleware(self.dummy_get_response).process_request(request)
6065
request.user = user
@@ -72,26 +77,43 @@ def test_form_valid(self, user: User, rf: RequestFactory):
7277

7378

7479
class TestUserRedirectView:
80+
"""
81+
Tests for the UserRedirectView.
82+
"""
83+
7584
def test_get_redirect_url(self, user: User, rf: RequestFactory):
85+
"""
86+
Test that UserRedirectView redirects to the "sde_collections:list" URL.
87+
"""
7688
view = UserRedirectView()
77-
request = rf.get("/fake-url")
89+
request = rf.get("/fake-url/")
7890
request.user = user
79-
8091
view.request = request
8192

82-
assert view.get_redirect_url() == f"/users/{user.username}/"
93+
expected_url = reverse("sde_collections:list")
94+
assert view.get_redirect_url() == expected_url, f"Expected {expected_url}, got {view.get_redirect_url()}"
8395

8496

8597
class TestUserDetailView:
98+
"""
99+
Tests for the user_detail_view function.
100+
"""
101+
86102
def test_authenticated(self, user: User, rf: RequestFactory):
103+
"""
104+
Test that an authenticated user can access their detail view.
105+
"""
87106
request = rf.get("/fake-url/")
88-
request.user = UserFactory()
107+
request.user = user
89108

90109
response = user_detail_view(request, username=user.username)
91110

92111
assert response.status_code == 200
93112

94113
def test_not_authenticated(self, user: User, rf: RequestFactory):
114+
"""
115+
Test that an unauthenticated user is redirected to the login page.
116+
"""
95117
request = rf.get("/fake-url/")
96118
request.user = AnonymousUser()
97119

0 commit comments

Comments
 (0)