Skip to content
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

Add test for jobs #4404

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
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
9 changes: 9 additions & 0 deletions tests/unit/jobs/test_apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import unittest
from django.apps import apps
from jobs.apps import JobsConfig


class JobsConfigTest(unittest.TestCase):
def test_apps_config(self):
self.assertEqual(JobsConfig.name, "jobs")
self.assertEqual(apps.get_app_config('jobs').name, 'jobs')
84 changes: 84 additions & 0 deletions tests/unit/jobs/test_aws_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import base64
import unittest
from unittest.mock import patch, MagicMock
from jobs.aws_utils import generate_aws_eks_bearer_token
import re


class TestGenerateAWSEksBearerToken(unittest.TestCase):

@patch("jobs.aws_utils.get_aws_credentials_for_challenge")
@patch("jobs.aws_utils.boto3.Session")
@patch("jobs.aws_utils.RequestSigner")
def test_generate_aws_eks_bearer_token(
self, MockRequestSigner, MockSession, MockGetAwsCredentials
):
# Mock AWS credentials
MockGetAwsCredentials.return_value = {
"AWS_ACCESS_KEY_ID": "fake_access_key",
"AWS_SECRET_ACCESS_KEY": "fake_secret_key",
"AWS_REGION": "us-west-2",
}

# Mock the Session and its client method
mock_session = MagicMock()
mock_client = MagicMock()
mock_session.client.return_value = mock_client
mock_client.meta.service_model.service_id = "STS"
MockSession.return_value = mock_session

# Mock RequestSigner and its generate_presigned_url method
mock_signer = MagicMock()
mock_signer.generate_presigned_url.return_value = "https://signed.url"
MockRequestSigner.return_value = mock_signer

# Define test input
cluster_name = "test-cluster"

class Challenge:
id = "challenge-id"

challenge = Challenge()

# Call the function to test
token = generate_aws_eks_bearer_token(cluster_name, challenge)

# Expected results
expected_signed_url = "https://signed.url"
expected_base64_url = base64.urlsafe_b64encode(
expected_signed_url.encode("utf-8")
).decode("utf-8")
expected_bearer_token = "k8s-aws-v1." + re.sub(
r"=*", "", expected_base64_url
)

# Assertions
MockGetAwsCredentials.assert_called_once_with("challenge-id")
MockSession.assert_called_once_with(
aws_access_key_id="fake_access_key",
aws_secret_access_key="fake_secret_key",
)
mock_session.client.assert_called_once_with(
"sts", region_name="us-west-2"
)
MockRequestSigner.assert_called_once_with(
"STS",
"us-west-2",
"sts",
"v4",
mock_session.get_credentials(),
mock_session.events,
)
mock_signer.generate_presigned_url.assert_called_once_with(
{
"method": "GET",
"url": "https://sts.us-west-2.amazonaws.com/?Action=GetCallerIdentity&Version=2011-06-15",
"body": {},
"headers": {"x-k8s-aws-id": cluster_name},
"context": {},
},
region_name="us-west-2",
expires_in=60,
operation_name="",
)
self.assertEqual(token, expected_bearer_token)
50 changes: 50 additions & 0 deletions tests/unit/jobs/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
from hosts.models import ChallengeHostTeam
from jobs.models import Submission
from participants.models import ParticipantTeam
import pytest
import rest_framework


class BaseTestCase(TestCase):
Expand Down Expand Up @@ -80,3 +82,51 @@ def test__str__(self):
self.assertEqual(
"{}".format(self.submission.id), self.submission.__str__()
)


@pytest.mark.django_db
class TestSubmissionModel:
def setup_method(self, method):
self.user = User.objects.create_user(username='testuser', password='password')
self.challenge_host_team = ChallengeHostTeam.objects.create(team_name="Test Challenge Host Team", created_by=self.user)
self.challenge = Challenge.objects.create(
title="Test Challenge",
description="Description for test challenge",
terms_and_conditions="Terms and conditions for test challenge",
submission_guidelines="Submission guidelines for test challenge",
creator=self.challenge_host_team,
start_date=timezone.now() - timedelta(days=2),
end_date=timezone.now() + timedelta(days=1),
published=False,
enable_forum=True,
anonymous_leaderboard=False)
self.challenge_phase = ChallengePhase.objects.create(
name='Test Phase',
challenge=self.challenge,
max_submissions=5,
max_submissions_per_day=2,
max_submissions_per_month=10)
self.participant_team = ParticipantTeam.objects.create(
team_name="Test Participant Team", created_by=self.user)

def test_max_submissions_per_day_reached(self):
for _ in range(self.challenge_phase.max_submissions_per_day):
Submission.objects.create(
participant_team=self.participant_team,
challenge_phase=self.challenge_phase,
created_by=self.user,
status=Submission.SUBMITTED,
input_file=None,
is_public=True,
submitted_at=timezone.now().replace(hour=0, minute=0, second=0, microsecond=0),
)
with pytest.raises(rest_framework.exceptions.PermissionDenied, match=r"{'error': ErrorDetail\(string='The maximum number of submission for today has been reached', code='permission_denied'\)}"):
Submission.objects.create(
participant_team=self.participant_team,
challenge_phase=self.challenge_phase,
created_by=self.user,
status=Submission.SUBMITTED,
input_file=None,
is_public=True,
submitted_at=timezone.now().replace(hour=0, minute=0, second=0, microsecond=0),
)
232 changes: 232 additions & 0 deletions tests/unit/jobs/test_sender.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
import os
from challenges.models import Challenge
import pytest
from unittest.mock import patch, MagicMock
from jobs.sender import publish_submission_message
import botocore
from jobs.sender import get_or_create_sqs_queue


@pytest.fixture
def message():
return {
"challenge_pk": 1,
"phase_pk": 1,
"submission_pk": 1,
"submitted_image_uri": "http://example.com/image",
"is_static_dataset_code_upload_submission": False,
}


@patch("jobs.sender.Challenge.objects.get")
@patch("jobs.sender.logger")
def test_publish_submission_message_challenge_does_not_exist(mock_logger, mock_challenge_get, message):
# Simulate Challenge.DoesNotExist exception
mock_challenge_get.side_effect = Challenge.DoesNotExist

response = publish_submission_message(message)

# Assert logger.exception is called with the correct message
mock_logger.exception.assert_called_once_with(
"Challenge does not exist for the given id {}".format(message["challenge_pk"])
)
# Assert the function returns None
assert response is None


@patch("jobs.sender.get_or_create_sqs_queue")
@patch("jobs.sender.increment_statsd_counter")
@patch("jobs.sender.get_submission_model")
@patch("jobs.sender.send_slack_notification")
@patch("jobs.sender.Challenge.objects.get")
def test_publish_submission_message_success(
mock_challenge_get,
mock_send_slack_notification,
mock_get_submission_model,
mock_increment_statsd_counter,
mock_get_or_create_sqs_queue,
message
):
# Mock Challenge object
mock_challenge = MagicMock()
mock_challenge.queue = "test-queue"
mock_challenge.slack_webhook_url = "http://slack-webhook-url"
mock_challenge.remote_evaluation = False
mock_challenge.title = "Test Challenge"
mock_challenge_get.return_value = mock_challenge

# Mock SQS queue
mock_queue = MagicMock()
mock_queue.send_message.return_value = {"MessageId": "12345"}
mock_get_or_create_sqs_queue.return_value = mock_queue

# Mock Submission object
mock_submission = MagicMock()
mock_submission.participant_team.team_name = "Test Team"
mock_submission.challenge_phase.name = "Test Phase"
mock_get_submission_model.return_value = mock_submission

response = publish_submission_message(message)

# Assert send_slack_notification is called with the correct parameters
expected_slack_message = {
"text": "A *new submission* has been uploaded to Test Challenge",
"fields": [
{"title": "Challenge Phase", "value": "Test Phase", "short": True},
{"title": "Participant Team Name", "value": "Test Team", "short": True},
{"title": "Submission Id", "value": message["submission_pk"], "short": True},
],
}
mock_send_slack_notification.assert_called_once_with(mock_challenge.slack_webhook_url, expected_slack_message)

# Assert the function returns the SQS response
assert response == {"MessageId": "12345"}


@patch("jobs.sender.boto3.resource")
@patch("jobs.sender.settings")
def test_get_or_create_sqs_queue_use_host_sqs(mock_settings, mock_boto3_resource):
mock_settings.DEBUG = False
mock_settings.TEST = False

mock_challenge = MagicMock()
mock_challenge.use_host_sqs = True
mock_challenge.queue_aws_region = "us-west-2"
mock_challenge.aws_secret_access_key = "secret"
mock_challenge.aws_access_key_id = "key"

mock_sqs = MagicMock()
mock_boto3_resource.return_value = mock_sqs

queue_name = "test-queue"
queue = get_or_create_sqs_queue(queue_name, mock_challenge)

mock_boto3_resource.assert_called_once_with(
"sqs",
region_name=mock_challenge.queue_aws_region,
aws_secret_access_key=mock_challenge.aws_secret_access_key,
aws_access_key_id=mock_challenge.aws_access_key_id,
)
assert queue == mock_sqs.get_queue_by_name.return_value


@patch("jobs.sender.boto3.resource")
@patch("jobs.sender.settings")
def test_get_or_create_sqs_queue_challenge_use_host_sqs(mock_settings, mock_boto3_resource):
# Test case where challenge.use_host_sqs is True
mock_settings.DEBUG = False
mock_settings.TEST = False

mock_challenge = MagicMock()
mock_challenge.use_host_sqs = True
mock_challenge.queue_aws_region = 'us-east-1'
mock_challenge.aws_access_key_id = 'foobar_key'
mock_challenge.aws_secret_access_key = 'foobar_secret'

mock_sqs = MagicMock()
mock_boto3_resource.return_value = mock_sqs

queue_name = "test-queue"
queue = get_or_create_sqs_queue(queue_name, mock_challenge)

mock_boto3_resource.assert_called_once_with(
"sqs",
region_name='us-east-1',
aws_access_key_id='foobar_key',
aws_secret_access_key='foobar_secret',
)
assert queue == mock_sqs.get_queue_by_name.return_value


@patch("jobs.sender.boto3.resource")
@patch("jobs.sender.settings")
def test_get_or_create_sqs_queue_no_challenge(mock_settings, mock_boto3_resource):
# Test case where challenge is None
mock_settings.DEBUG = False
mock_settings.TEST = False

mock_sqs = MagicMock()
mock_boto3_resource.return_value = mock_sqs

queue_name = "test-queue"
queue = get_or_create_sqs_queue(queue_name)

mock_boto3_resource.assert_called_once_with(
"sqs",
region_name=os.environ.get("AWS_DEFAULT_REGION", "us-east-1"),
aws_access_key_id=os.environ.get("AWS_ACCESS_KEY_ID"),
aws_secret_access_key=os.environ.get("AWS_SECRET_ACCESS_KEY"),
)
assert queue == mock_sqs.get_queue_by_name.return_value


@patch("jobs.sender.boto3.resource")
@patch("jobs.sender.settings")
def test_get_or_create_sqs_queue_non_existent_queue(mock_settings, mock_boto3_resource):
# Test case where the queue does not exist, and it gets created
mock_settings.DEBUG = False
mock_settings.TEST = False

mock_challenge = MagicMock()
mock_challenge.use_host_sqs = False
mock_challenge.sqs_retention_period = "1209600"

mock_sqs = MagicMock()
mock_boto3_resource.return_value = mock_sqs

mock_sqs.get_queue_by_name.side_effect = botocore.exceptions.ClientError(
{"Error": {"Code": "AWS.SimpleQueueService.NonExistentQueue"}}, "GetQueueUrl"
)

mock_created_queue = MagicMock()
mock_sqs.create_queue.return_value = mock_created_queue

queue_name = "test-queue"
queue = get_or_create_sqs_queue(queue_name, mock_challenge)

mock_sqs.create_queue.assert_called_once_with(
QueueName=queue_name,
Attributes={"MessageRetentionPeriod": mock_challenge.sqs_retention_period},
)
assert queue == mock_created_queue


@patch("jobs.sender.boto3.resource")
@patch("jobs.sender.settings")
def test_get_or_create_sqs_queue_debug_or_test(mock_settings, mock_boto3_resource):
# Test case where settings.DEBUG or settings.TEST is True
mock_settings.DEBUG = True
mock_settings.TEST = False

mock_sqs = MagicMock()
mock_boto3_resource.return_value = mock_sqs

queue_name = "test-queue"
queue = get_or_create_sqs_queue(queue_name)

mock_boto3_resource.assert_called_once_with(
"sqs",
endpoint_url=os.environ.get("AWS_SQS_ENDPOINT", "http://sqs:9324"),
region_name=os.environ.get("AWS_DEFAULT_REGION", "us-east-1"),
aws_secret_access_key=os.environ.get("AWS_SECRET_ACCESS_KEY", "x"),
aws_access_key_id=os.environ.get("AWS_ACCESS_KEY_ID", "x"),
)
assert queue_name != "evalai_submission_queue" # 'queue_name' is not modified in the test, so assert the original value was passed correctly
assert queue # Ensure queue was returned


@patch("jobs.sender.boto3.resource")
@patch("jobs.sender.settings")
def test_get_or_create_sqs_queue_empty_queue_name(mock_settings, mock_boto3_resource):
# Test case where queue_name is empty
mock_settings.DEBUG = False
mock_settings.TEST = False

mock_sqs = MagicMock()
mock_boto3_resource.return_value = mock_sqs

queue_name = ""
queue = get_or_create_sqs_queue(queue_name)

mock_sqs.get_queue_by_name.assert_called_once_with(QueueName="evalai_submission_queue")
assert queue # Ensure queue was returned
Loading