Skip to content

Commit

Permalink
Add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
amickan committed May 29, 2024
1 parent aea09f5 commit a47186e
Show file tree
Hide file tree
Showing 8 changed files with 183 additions and 106 deletions.
10 changes: 10 additions & 0 deletions app/grandchallenge/algorithms/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,16 @@ def public_test_case(self):
except AttributeError:
return False

def form_field_label(self):
title = f"{self.title}"
if self.active_image:
title += f" (Active image: {self.active_image.pk})"
if self.active_model:
title += f" (Active model: {self.active_model.pk})"
else:
title += " (Active model: None)"
return title


class AlgorithmUserObjectPermission(UserObjectPermissionBase):
content_object = models.ForeignKey(Algorithm, on_delete=models.CASCADE)
Expand Down
2 changes: 1 addition & 1 deletion app/grandchallenge/algorithms/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,8 @@ def retry_with_delay():
def create_algorithm_jobs(
*,
algorithm_image,
algorithm_model,
civ_sets,
algorithm_model=None,
extra_viewer_groups=None,
extra_logs_viewer_groups=None,
max_jobs=None,
Expand Down
7 changes: 6 additions & 1 deletion app/grandchallenge/evaluation/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,11 @@ class Meta:
)


class AlgorithmChoiceField(ModelChoiceField):
def label_from_instance(self, obj):
return obj.form_field_label()


submission_fields = (
"creator",
"phase",
Expand Down Expand Up @@ -294,7 +299,7 @@ class SubmissionForm(
label="Predictions File",
queryset=None,
)
algorithm = ModelChoiceField(queryset=None)
algorithm = AlgorithmChoiceField(queryset=None)

def __init__(self, *args, user, phase: Phase, **kwargs): # noqa: C901
super().__init__(*args, user=user, phase=phase, **kwargs)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,11 @@ <h2>Evaluation</h2>
<dd>
<a href="{{ object.submission.algorithm_image.algorithm.get_absolute_url }}">
{{ object.submission.algorithm_image.algorithm.title }}
(Version {{ object.submission.algorithm_image.pk }})
(Image Version {{ object.submission.algorithm_image.pk }}
{% if object.submission.algorithm_model %}
Model version {{ object.submission.algorithm_model.pk }}
{% endif %}
)
</a>
</dd>
{% endif %}
Expand Down
52 changes: 0 additions & 52 deletions app/tests/algorithms_tests/test_permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,10 @@
from grandchallenge.evaluation.tasks import (
create_algorithm_jobs_for_evaluation,
)
from grandchallenge.verifications.models import Verification
from tests.algorithms_tests.factories import (
AlgorithmFactory,
AlgorithmImageFactory,
AlgorithmJobFactory,
AlgorithmModelFactory,
)
from tests.algorithms_tests.utils import TwoAlgorithms
from tests.archives_tests.factories import ArchiveFactory, ArchiveItemFactory
Expand Down Expand Up @@ -536,53 +534,3 @@ def test_job_permissions_for_debug_phase(
)
# No-one should be in the viewers group
assert {*job.viewers.user_set.all()} == set()


@pytest.mark.django_db
def test_algorithm_model_view_permissions(client):
user, editor = UserFactory.create_batch(2)
alg = AlgorithmFactory()
alg.add_editor(editor)

am = AlgorithmModelFactory(algorithm=alg)

response = get_view_for_user(
viewname="algorithms:model-create",
client=client,
reverse_kwargs={"slug": alg.slug},
user=user,
)
assert response.status_code == 403

response = get_view_for_user(
viewname="algorithms:model-detail",
client=client,
reverse_kwargs={"slug": alg.slug, "pk": am.pk},
user=user,
)
assert response.status_code == 403

response = get_view_for_user(
viewname="algorithms:model-create",
client=client,
reverse_kwargs={"slug": alg.slug},
user=editor,
)
assert response.status_code == 403

Verification.objects.create(user=editor, is_verified=True)
response = get_view_for_user(
viewname="algorithms:model-create",
client=client,
reverse_kwargs={"slug": alg.slug},
user=editor,
)
assert response.status_code == 200

response = get_view_for_user(
viewname="algorithms:model-detail",
client=client,
reverse_kwargs={"slug": alg.slug, "pk": am.pk},
user=editor,
)
assert response.status_code == 200
58 changes: 58 additions & 0 deletions app/tests/algorithms_tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
AlgorithmFactory,
AlgorithmImageFactory,
AlgorithmJobFactory,
AlgorithmModelFactory,
AlgorithmPermissionRequestFactory,
)
from tests.cases_tests import RESOURCE_PATH
Expand Down Expand Up @@ -326,6 +327,7 @@ def test_algorithm_jobs_list_view(client):
class TestObjectPermissionRequiredViews:
def test_permission_required_views(self, client):
ai = AlgorithmImageFactory(is_manifest_valid=True, is_in_registry=True)
am = AlgorithmModelFactory()
u = UserFactory()
j = AlgorithmJobFactory(algorithm_image=ai, status=Job.SUCCESS)
p = AlgorithmPermissionRequestFactory(algorithm=ai.algorithm)
Expand Down Expand Up @@ -442,6 +444,20 @@ def test_permission_required_views(self, client):
ai.algorithm,
None,
),
(
"model-create",
{"slug": am.algorithm.slug},
"change_algorithm",
am.algorithm,
None,
),
(
"model-detail",
{"slug": am.algorithm.slug, "pk": am.pk},
"view_algorithmmodel",
am,
None,
),
]:

def _get_view():
Expand Down Expand Up @@ -1242,3 +1258,45 @@ def test_evaluations_are_filtered(client):
)

assert [*response.context["best_evaluation_per_phase"]] == [e, e2]


@pytest.mark.django_db
def test_job_create_denied_for_same_input_model_and_image(client):
creator = UserFactory()
VerificationFactory(user=creator, is_verified=True)
alg = AlgorithmFactory()
alg.add_editor(user=creator)
ci = ComponentInterfaceFactory(
kind=InterfaceKind.InterfaceKindChoices.IMAGE
)
alg.inputs.set([ci])
ai = AlgorithmImageFactory(
algorithm=alg,
is_manifest_valid=True,
is_in_registry=True,
is_desired_version=True,
)
am = AlgorithmModelFactory(algorithm=alg, is_desired_version=True)
im = ImageFactory()
assign_perm("view_image", creator, im)
civ = ComponentInterfaceValueFactory(interface=ci, image=im)
j = AlgorithmJobFactory(algorithm_image=ai, algorithm_model=am)
j.inputs.set([civ])
response = get_view_for_user(
viewname="algorithms:job-create",
client=client,
method=client.post,
reverse_kwargs={
"slug": alg.slug,
},
user=creator,
data={
ci.slug: im.pk,
f"WidgetChoice-{ci.slug}": WidgetChoices.IMAGE_SEARCH.name,
},
)
assert not response.context["form"].is_valid()
assert (
"A result for these inputs with the current image and model already exists."
in str(response.context["form"].errors)
)
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ def test_execute(settings):
executor = AmazonSageMakerTrainingExecutor(
job_id=f"algorithms-job-{pk}",
exec_image_repo_tag="",
algorithm_model=None,
memory_limit=4,
time_limit=60,
requires_gpu=False,
Expand Down Expand Up @@ -196,6 +197,7 @@ def test_execute(settings):
"PYTHONUNBUFFERED": "1",
"no_proxy": "amazonaws.com",
"GRAND_CHALLENGE_COMPONENT_WRITABLE_DIRECTORIES": "/opt/ml/output/data:/opt/ml/model:/opt/ml/checkpoints:/tmp",
"GRAND_CHALLENGE_COMPONENT_POST_CLEAN_DIRECTORIES": "/opt/ml/output/data:/opt/ml/model",
},
"VpcConfig": {
"SecurityGroupIds": [
Expand Down
Loading

0 comments on commit a47186e

Please sign in to comment.