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
68 changes: 50 additions & 18 deletions .github/workflows/hypha-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,14 @@ jobs:
- name: Run pre-commit
uses: pre-commit/action@v3.0.1

test-be:
django-checks:
runs-on: ubuntu-latest
timeout-minutes: 10
timeout-minutes: 5
env:
DATABASE_URL: postgres://hypha:hypha@localhost/hypha?sslmode=disable
DJANGO_SETTINGS_MODULE: hypha.settings.test
SEND_MESSAGES: false
PYTHONDONTWRITEBYTECODE: 1
APPLICATION_TRANSLATIONS_ENABLED: 1 # Run tests for machine translation logic

services:
postgres:
Expand All @@ -51,10 +50,6 @@ jobs:
--health-timeout 5s
--health-retries 5

strategy:
matrix:
group: [1, 2, 3]

steps:
- uses: actions/checkout@v6

Expand All @@ -64,28 +59,65 @@ jobs:
- name: Install python dependencies
run: |
uv venv
uv sync --frozen --group translate
uv sync --frozen

- name: Create static_compiled dir
run: |
mkdir hypha/static_compiled
run: mkdir hypha/static_compiled

- name: Check Django migrations
if: matrix.group == 1
run: |
uv run python manage.py makemigrations --dry-run --verbosity=3
uv run python manage.py makemigrations --check

- name: Run django collectstatic
if: matrix.group == 2
run: |
uv run python manage.py collectstatic --noinput --no-post-process --verbosity=1
run: uv run python manage.py collectstatic --noinput --no-post-process --verbosity=1

- name: Check Django setup
run: uv run python manage.py check

test-be:
runs-on: ubuntu-latest
timeout-minutes: 10
env:
DATABASE_URL: postgres://hypha:hypha@localhost/hypha?sslmode=disable
DJANGO_SETTINGS_MODULE: hypha.settings.test
SEND_MESSAGES: false
PYTHONDONTWRITEBYTECODE: 1
APPLICATION_TRANSLATIONS_ENABLED: 1 # Run tests for machine translation logic

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

strategy:
matrix:
group: [1, 2, 3]

- name: Check Django Setup
if: matrix.group == 3
steps:
- uses: actions/checkout@v6

- name: Install uv
uses: astral-sh/setup-uv@v7

- name: Install python dependencies
run: |
uv run python manage.py check
uv venv
uv sync --frozen --group translate

- name: Create static_compiled dir
run: mkdir hypha/static_compiled

- name: Run pytest
run: |
uv run pytest --splits 3 --group ${{ matrix.group }}
uv run pytest --splits 3 --group ${{ matrix.group }} --durations-path=.test_durations
1,628 changes: 814 additions & 814 deletions .test_durations

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ py-test: .cache/py-packages ## Run Python tests with pytest, including coverage

@echo "Removing test files generated during test"
@find media/ -iname 'test_*.pdf' -delete
@find media/ -iname 'test_*.png' -delete
@find media/ -iname '*.dat' -delete
@find media/ -name '.DS_Store' -delete
@find media/ -type d -empty -delete
@rm -rf media/temp_uploads/*

Expand Down
30 changes: 19 additions & 11 deletions hypha/apply/funds/tests/factories/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from hypha.apply.funds import blocks
from hypha.apply.stream_forms.testing.factories import (
BLOCK_FACTORY_DEFINITION,
NON_FILE_BLOCK_FACTORY_DEFINITION,
FormFieldBlockFactory,
ParagraphBlockFactory,
StreamFieldUUIDFactory,
Expand All @@ -14,6 +15,7 @@

__all__ = [
"CustomFormFieldsFactory",
"NonFileCustomFormFieldsFactory",
"TitleBlockFactory",
"EmailBlockFactory",
"FullNameBlockFactory",
Expand Down Expand Up @@ -124,16 +126,22 @@ def make_form_answer(cls, params=None):
return address


_CUSTOM_FORM_FIELDS = {
"duration": factory.SubFactory(DurationBlockFactory),
"title": factory.SubFactory(TitleBlockFactory),
"value": factory.SubFactory(ValueFieldBlockFactory),
"email": factory.SubFactory(EmailBlockFactory),
"address": factory.SubFactory(AddressFieldBlockFactory),
"full_name": factory.SubFactory(FullNameBlockFactory),
"text_markup": factory.SubFactory(ParagraphBlockFactory),
"rich_text": factory.SubFactory(RichTextFieldBlockFactory),
}

CustomFormFieldsFactory = StreamFieldUUIDFactory(
{
**BLOCK_FACTORY_DEFINITION,
"duration": factory.SubFactory(DurationBlockFactory),
"title": factory.SubFactory(TitleBlockFactory),
"value": factory.SubFactory(ValueFieldBlockFactory),
"email": factory.SubFactory(EmailBlockFactory),
"address": factory.SubFactory(AddressFieldBlockFactory),
"full_name": factory.SubFactory(FullNameBlockFactory),
"text_markup": factory.SubFactory(ParagraphBlockFactory),
"rich_text": factory.SubFactory(RichTextFieldBlockFactory),
}
{**BLOCK_FACTORY_DEFINITION, **_CUSTOM_FORM_FIELDS}
)

# No file/image blocks — faster for tests that don't need file uploads
NonFileCustomFormFieldsFactory = StreamFieldUUIDFactory(
{**NON_FILE_BLOCK_FACTORY_DEFINITION, **_CUSTOM_FORM_FIELDS}
)
17 changes: 14 additions & 3 deletions hypha/apply/funds/tests/factories/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,10 @@ class ApplicationFormDataFactory(FormDataFactory):
field_factory = blocks.CustomFormFieldsFactory


class NonFileApplicationFormDataFactory(FormDataFactory):
field_factory = blocks.NonFileCustomFormFieldsFactory


class ApplicationSubmissionFactory(factory.django.DjangoModelFactory):
class Meta:
model = ApplicationSubmission
Expand All @@ -253,10 +257,17 @@ class Params:
workflow_stages = 1
rejected = factory.Trait(status="rejected")
with_external_review = False
with_files = factory.Trait(
form_fields=blocks.CustomFormFieldsFactory,
form_data=factory.SubFactory(
ApplicationFormDataFactory,
form_fields=factory.SelfAttribute("..form_fields"),
),
)

form_fields = blocks.CustomFormFieldsFactory
form_fields = blocks.NonFileCustomFormFieldsFactory
form_data = factory.SubFactory(
ApplicationFormDataFactory,
NonFileApplicationFormDataFactory,
form_fields=factory.SelfAttribute("..form_fields"),
)
page = factory.SelfAttribute(".round.fund")
Expand Down Expand Up @@ -359,7 +370,7 @@ class Meta:
"hypha.apply.funds.tests.factories.ApplicationSubmissionFactory"
)
form_data = factory.SubFactory(
ApplicationFormDataFactory,
NonFileApplicationFormDataFactory,
form_fields=factory.SelfAttribute("..submission.form_fields"),
for_factory=ApplicationSubmissionFactory,
clean=True,
Expand Down
8 changes: 5 additions & 3 deletions hypha/apply/funds/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,9 @@ def test_number_not_in_search(self):

def test_file_gets_uploaded(self):
filename = "test_image.png"
submission = self.make_submission(form_data__image__filename=filename)
submission = self.make_submission(
with_files=True, form_data__image__filename=filename
)
path = os.path.join(settings.MEDIA_ROOT, "submission", str(submission.id))

# Check we created the top level folder
Expand All @@ -464,7 +466,7 @@ def test_file_gets_uploaded(self):
self.assertIn(filename, found_files)

def test_correct_file_path_generated(self):
submission = ApplicationSubmissionFactory()
submission = ApplicationSubmissionFactory(with_files=True)

def check_generated_file_path(file_to_test, file_id):
file_path_generated = file_to_test.generate_filename()
Expand Down Expand Up @@ -583,7 +585,7 @@ def test_named_blocks_dont_break_if_no_response(self):
self.assertIsNone(submission.value)

def test_file_private_url_included(self):
submission = ApplicationSubmissionFactory()
submission = ApplicationSubmissionFactory(with_files=True)
answers = submission.output_answers()

def file_url_in_answers(file_to_test, file_id):
Expand Down
8 changes: 4 additions & 4 deletions hypha/apply/funds/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -1739,7 +1739,7 @@ class TestStaffSubmissionFileView(BaseSubmissionFileViewTestCase):
user_factory = StaffFactory

def test_staff_can_access(self):
submission = ApplicationSubmissionFactory()
submission = ApplicationSubmissionFactory(with_files=True)
response = self.get_page(submission)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.redirect_chain, [])
Expand All @@ -1749,13 +1749,13 @@ class TestUserSubmissionFileView(BaseSubmissionFileViewTestCase):
user_factory = ApplicantFactory

def test_owner_can_access(self):
submission = ApplicationSubmissionFactory(user=self.user)
submission = ApplicationSubmissionFactory(user=self.user, with_files=True)
response = self.get_page(submission)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.redirect_chain, [])

def test_user_can_not_access(self):
submission = ApplicationSubmissionFactory()
submission = ApplicationSubmissionFactory(with_files=True)
response = self.get_page(submission)
self.assertEqual(response.status_code, 403)
self.assertEqual(response.redirect_chain, [])
Expand All @@ -1765,7 +1765,7 @@ class TestAnonSubmissionFileView(BaseSubmissionFileViewTestCase):
user_factory = AnonymousUser

def test_anonymous_can_not_access(self):
submission = ApplicationSubmissionFactory()
submission = ApplicationSubmissionFactory(with_files=True)
response = self.get_page(submission)
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.redirect_chain), 1)
Expand Down
2 changes: 1 addition & 1 deletion hypha/apply/projects/tests/test_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ def test_add_new_supporting_document(self):
class TestSelectDocumentForm(TestCase):
def test_copying_files(self):
category = DocumentCategoryFactory()
project = ProjectFactory()
project = ProjectFactory(submission__with_files=True)

self.assertEqual(project.packet_files.count(), 0)

Expand Down
10 changes: 7 additions & 3 deletions hypha/apply/utils/testing/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,15 @@ class BaseViewTestCase(TestCase):
user_factory = None
user = None

@classmethod
def setUpTestData(cls):
super().setUpTestData()
if cls.user_factory:
cls.user = cls.user_factory()

def setUp(self):
self.factory = RequestFactory()
if self.user_factory:
self.user = self.user_factory()
else:
if not self.user_factory:
self.user = AnonymousUser()

if not self.user.is_anonymous:
Expand Down
Loading