Commit f3089e9e by Saleem Latif Committed by GitHub

Merge pull request #13783 from edx/saleem-latif/ENT-28

ENT-28: Add testing for 'saml' management command
parents 907801d6 38931957
......@@ -3,11 +3,45 @@ Tests for `saml` management command, this command fetches saml metadata from pro
existing data accordingly.
"""
import unittest
import os
import mock
from django.test import TestCase
from django.core.management import call_command
from django.core.management.base import CommandError
from django.conf import settings
from django.utils.six import StringIO
from requests.models import Response
from third_party_auth.tests.factories import SAMLConfigurationFactory, SAMLProviderConfigFactory
def mock_get(status_code=200):
"""
Args:
status_code (int): integer showing the status code for the response object.
Returns:
returns a function that can be used as a mock function for requests.get.
"""
def _(url=None, *args, **kwargs): # pylint: disable=unused-argument
"""
mock method for requests.get, this method will read xml file, form a Response object from the
contents of this file, set status code and return the Response object.
"""
url = url.split("/")[-1] if url else "testshib-providers.xml"
file_path = os.path.dirname(os.path.realpath(__file__)) + "/test_data/{}".format(url)
with open(file_path) as providers:
xml = providers.read()
response = Response()
response._content = xml # pylint: disable=protected-access
response.status_code = status_code
return response
return _
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
......@@ -15,6 +49,28 @@ class TestSAMLCommand(TestCase):
"""
Test django management command for fetching saml metadata.
"""
def setUp(self):
"""
Setup operations for saml configurations. these operations contain
creation of SAMLConfiguration and SAMLProviderConfig records in database.
"""
super(TestSAMLCommand, self).setUp()
self.stdout = StringIO()
# We are creating SAMLConfiguration instance here so that there is always at-least one
# disabled saml configuration instance, this is done to verify that disabled configurations are
# not processed.
SAMLConfigurationFactory.create(enabled=False)
SAMLProviderConfigFactory.create()
def __create_saml_configurations__(self, saml_config=None, saml_provider_config=None):
"""
Helper method to create SAMLConfiguration and AMLProviderConfig.
"""
SAMLConfigurationFactory.create(enabled=True, **(saml_config or {}))
SAMLProviderConfigFactory.create(enabled=True, **(saml_provider_config or {}))
def test_raises_command_error_for_invalid_arguments(self):
"""
Test that management command raises `CommandError` with a proper message in case of
......@@ -29,3 +85,82 @@ class TestSAMLCommand(TestCase):
# Call `saml` command without any argument so that it raises a CommandError
with self.assertRaisesMessage(CommandError, "Command can only be used with '--pull' option."):
call_command("saml", pull=False)
def test_no_saml_configuration(self):
"""
Test that management command completes without errors and logs correct information when no
saml configurations are enabled/present.
"""
# Capture command output log for testing.
call_command("saml", pull=True, stdout=self.stdout)
self.assertIn('Done. Fetched 0 total. 0 were updated and 0 failed.', self.stdout.getvalue())
@mock.patch("requests.get", mock_get())
def test_fetch_saml_metadata(self):
"""
Test that management command completes without errors and logs correct information when
one or more saml configurations are enabled.
"""
# Create enabled configurations
self.__create_saml_configurations__()
# Capture command output log for testing.
call_command("saml", pull=True, stdout=self.stdout)
self.assertIn('Done. Fetched 1 total. 1 were updated and 0 failed.', self.stdout.getvalue())
@mock.patch("requests.get", mock_get(status_code=404))
def test_fetch_saml_metadata_failure(self):
"""
Test that management command completes with proper message for errors
and logs correct information.
"""
# Create enabled configurations
self.__create_saml_configurations__()
# Capture command output log for testing.
call_command("saml", pull=True, stdout=self.stdout)
self.assertIn('Done. Fetched 1 total. 0 were updated and 1 failed.', self.stdout.getvalue())
@mock.patch("requests.get", mock_get(status_code=200))
def test_fetch_multiple_providers_data(self):
"""
Test that management command completes with proper message for error or success
and logs correct information when there are multiple providers with their data.
"""
# Create enabled configurations
self.__create_saml_configurations__()
# Add another set of configurations
self.__create_saml_configurations__(
saml_config={
"site__domain": "second.testserver.fake",
},
saml_provider_config={
"site__domain": "second.testserver.fake",
"idp_slug": "second-test-shib",
"entity_id": "https://idp.testshib.org/idp/another-shibboleth",
"metadata_source": "https://www.testshib.org/metadata/another-testshib-providers.xml",
}
)
# Add another set of configurations
self.__create_saml_configurations__(
saml_config={
"site__domain": "third.testserver.fake",
},
saml_provider_config={
"site__domain": "third.testserver.fake",
"idp_slug": "third-test-shib",
# Note: This entity id will not be present in returned response and will cause failed update.
"entity_id": "https://idp.testshib.org/idp/non-existent-shibboleth",
"metadata_source": "https://www.testshib.org/metadata/third/testshib-providers.xml",
}
)
# Capture command output log for testing.
call_command("saml", pull=True, stdout=self.stdout)
self.assertIn('Done. Fetched 3 total. 2 were updated and 1 failed.', self.stdout.getvalue())
"""
Provides factories for third_party_auth models.
"""
from factory import SubFactory
from factory.django import DjangoModelFactory
from third_party_auth.models import SAMLConfiguration, SAMLProviderConfig
from openedx.core.djangoapps.site_configuration.tests.factories import SiteFactory
class SAMLConfigurationFactory(DjangoModelFactory):
"""
Factory or SAMLConfiguration model in third_party_auth app.
"""
class Meta(object):
model = SAMLConfiguration
site = SubFactory(SiteFactory)
enabled = True
class SAMLProviderConfigFactory(DjangoModelFactory):
"""
Factory or SAMLProviderConfig model in third_party_auth app.
"""
class Meta(object):
model = SAMLProviderConfig
django_get_or_create = ('idp_slug', 'metadata_source', "entity_id")
site = SubFactory(SiteFactory)
enabled = True
idp_slug = "test-shib"
name = "TestShib College"
entity_id = "https://idp.testshib.org/idp/shibboleth"
metadata_source = "https://www.testshib.org/metadata/testshib-providers.xml"
......@@ -24,6 +24,7 @@ class SiteFactory(DjangoModelFactory):
"""
class Meta(object):
model = Site
django_get_or_create = ('domain',)
domain = 'testserver.fake'
name = 'testserver.fake'
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment