Skip to content
Merged
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
11 changes: 11 additions & 0 deletions lms/djangoapps/badges/service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"""
Badging service for XBlocks
"""
from badges.models import BadgeClass


class BadgingService(object):
"""
A class that provides functions for managing badges which XBlocks can use.
"""
get_badge_class = BadgeClass.get_badge_class
40 changes: 18 additions & 22 deletions lms/djangoapps/badges/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,15 @@
from student.tests.factories import UserFactory


class ImageFetchingMixin(object):
def get_image(name):
"""
Provides the ability to grab a badge image from the test data root.
Get one of the test images from the test data directory.
"""
def get_image(self, name):
"""
Get one of the test images from the test data directory.
"""
return ImageFile(open(TEST_DATA_ROOT / 'badges' / name + '.png'))
return ImageFile(open(TEST_DATA_ROOT / 'badges' / name + '.png'))


@attr('shard_1')
class BadgeImageConfigurationTest(TestCase, ImageFetchingMixin):
class BadgeImageConfigurationTest(TestCase):
"""
Test the validation features of BadgeImageConfiguration.
"""
Expand All @@ -38,10 +34,10 @@ def test_no_double_default(self):
"""
Verify that creating two configurations as default is not permitted.
"""
CourseCompleteImageConfiguration(mode='test', icon=self.get_image('good'), default=True).save()
CourseCompleteImageConfiguration(mode='test', icon=get_image('good'), default=True).save()
self.assertRaises(
ValidationError,
CourseCompleteImageConfiguration(mode='test2', icon=self.get_image('good'), default=True).full_clean
CourseCompleteImageConfiguration(mode='test2', icon=get_image('good'), default=True).full_clean
)

def test_runs_validator(self):
Expand All @@ -50,7 +46,7 @@ def test_runs_validator(self):
"""
self.assertRaises(
ValidationError,
CourseCompleteImageConfiguration(mode='test2', icon=self.get_image('unbalanced')).full_clean
CourseCompleteImageConfiguration(mode='test2', icon=get_image('unbalanced')).full_clean
)


Expand All @@ -60,7 +56,7 @@ class DummyBackend(object):
"""


class BadgeClassTest(ModuleStoreTestCase, ImageFetchingMixin):
class BadgeClassTest(ModuleStoreTestCase):
"""
Test BadgeClass functionality
"""
Expand All @@ -80,7 +76,7 @@ def test_get_badge_class_preexisting(self):
# Ignore additional parameters. This class already exists.
badge_class = BadgeClass.get_badge_class(
slug='test_slug', issuing_component='test_component', description='Attempted override',
criteria='test', display_name='Testola', image_file_handle=self.get_image('good')
criteria='test', display_name='Testola', image_file_handle=get_image('good')
)
# These defaults are set on the factory.
self.assertEqual(badge_class.criteria, 'https://example.com/syllabus')
Expand All @@ -97,11 +93,11 @@ def test_unique_for_course(self):
premade_badge_class = BadgeClassFactory.create(course_id=course_key)
badge_class = BadgeClass.get_badge_class(
slug='test_slug', issuing_component='test_component', description='Attempted override',
criteria='test', display_name='Testola', image_file_handle=self.get_image('good')
criteria='test', display_name='Testola', image_file_handle=get_image('good')
)
course_badge_class = BadgeClass.get_badge_class(
slug='test_slug', issuing_component='test_component', description='Attempted override',
criteria='test', display_name='Testola', image_file_handle=self.get_image('good'),
criteria='test', display_name='Testola', image_file_handle=get_image('good'),
course_id=course_key,
)
self.assertNotEqual(badge_class.id, course_badge_class.id)
Expand All @@ -114,7 +110,7 @@ def test_get_badge_class_create(self):
badge_class = BadgeClass.get_badge_class(
slug='new_slug', issuing_component='new_component', description='This is a test',
criteria='https://example.com/test_criteria', display_name='Super Badge',
image_file_handle=self.get_image('good')
image_file_handle=get_image('good')
)
# This should have been saved before being passed back.
self.assertTrue(badge_class.id)
Expand Down Expand Up @@ -152,7 +148,7 @@ def test_get_badge_class_validate(self):
BadgeClass.get_badge_class,
slug='new_slug', issuing_component='new_component', description='This is a test',
criteria='https://example.com/test_criteria', display_name='Super Badge',
image_file_handle=self.get_image('unbalanced')
image_file_handle=get_image('unbalanced')
)

def test_get_for_user(self):
Expand Down Expand Up @@ -185,7 +181,7 @@ def test_runs_validator(self):
ValidationError,
BadgeClass(
slug='test', issuing_component='test2', criteria='test3',
description='test4', image=self.get_image('unbalanced')
description='test4', image=get_image('unbalanced')
).full_clean
)

Expand Down Expand Up @@ -221,26 +217,26 @@ def test_assertions_for_user(self):
self.assertEqual(course_scoped_assertions, course_assertions)


class ValidBadgeImageTest(TestCase, ImageFetchingMixin):
class ValidBadgeImageTest(TestCase):
"""
Tests the badge image field validator.
"""
def test_good_image(self):
"""
Verify that saving a valid badge image is no problem.
"""
validate_badge_image(self.get_image('good'))
validate_badge_image(get_image('good'))

def test_unbalanced_image(self):
"""
Verify that setting an image with an uneven width and height raises an error.
"""
unbalanced = ImageFile(self.get_image('unbalanced'))
unbalanced = ImageFile(get_image('unbalanced'))
self.assertRaises(ValidationError, validate_badge_image, unbalanced)

def test_large_image(self):
"""
Verify that setting an image that is too big raises an error.
"""
large = self.get_image('large')
large = get_image('large')
self.assertRaises(ValidationError, validate_badge_image, large)
4 changes: 4 additions & 0 deletions lms/djangoapps/lms_xblock/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

from django.core.urlresolvers import reverse
from django.conf import settings

from badges.service import BadgingService
from request_cache.middleware import RequestCache
from lms.djangoapps.lms_xblock.models import XBlockAsidesConfig
from openedx.core.djangoapps.user_api.course_tag import api as user_course_tag_api
Expand Down Expand Up @@ -211,6 +213,8 @@ def __init__(self, **kwargs):
services['library_tools'] = LibraryToolsService(modulestore())
services['fs'] = xblock.reference.plugins.FSService()
services['settings'] = SettingsService()
if settings.FEATURES["ENABLE_OPENBADGES"]:
services['badging'] = BadgingService()
self.request_token = kwargs.pop('request_token', None)
super(LmsModuleSystem, self).__init__(**kwargs)

Expand Down
76 changes: 71 additions & 5 deletions lms/djangoapps/lms_xblock/test/test_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,20 @@
from django.contrib.auth.models import User
from django.conf import settings
from ddt import ddt, data
from mock import Mock
from unittest import TestCase
from django.test import TestCase
from mock import Mock, patch
from urlparse import urlparse

from opaque_keys.edx.keys import CourseKey
from opaque_keys.edx.locations import BlockUsageLocator, CourseLocator, SlashSeparatedCourseKey

from badges.tests.factories import BadgeClassFactory
from badges.tests.test_models import get_image
from lms.djangoapps.lms_xblock.runtime import quote_slashes, unquote_slashes, LmsModuleSystem
from xblock.fields import ScopeIds

from student.tests.factories import UserFactory

TEST_STRINGS = [
'',
'foobar',
Expand Down Expand Up @@ -138,9 +145,7 @@ class TestUserServiceAPI(TestCase):
def setUp(self):
super(TestUserServiceAPI, self).setUp()
self.course_id = SlashSeparatedCourseKey("org", "course", "run")

self.user = User(username='runtime_robot', email='runtime_robot@edx.org', password='test', first_name='Robot')
self.user.save()
self.user = UserFactory.create()

def mock_get_real_user(_anon_id):
"""Just returns the test user"""
Expand Down Expand Up @@ -181,3 +186,64 @@ def test_get_set_tag(self):
# Try to get tag in wrong scope
with self.assertRaises(ValueError):
self.runtime.service(self.mock_block, 'user_tags').get_tag('fake_scope', self.key)


class TestBadgingService(TestCase):
"""Test the badging service interface"""

def setUp(self):
super(TestBadgingService, self).setUp()
self.course_id = CourseKey.from_string('course-v1:org+course+run')

self.user = User(username='test_robot', email='test_robot@edx.org', password='test', first_name='Test')
self.user.save()

self.mock_block = Mock()
self.mock_block.service_declaration.return_value = 'needs'

def create_runtime(self):
"""
Create the testing runtime.
"""
def mock_get_real_user(_anon_id):
"""Just returns the test user"""
return self.user

return LmsModuleSystem(
static_url='/static',
track_function=Mock(),
get_module=Mock(),
render_template=Mock(),
replace_urls=str,
course_id=self.course_id,
get_real_user=mock_get_real_user,
descriptor_runtime=Mock(),
)

@patch.dict(settings.FEATURES, {'ENABLE_OPENBADGES': True})
def test_service_rendered(self):
runtime = self.create_runtime()
self.assertTrue(runtime.service(self.mock_block, 'badging'))

@patch.dict(settings.FEATURES, {'ENABLE_OPENBADGES': False})
def test_no_service_rendered(self):
runtime = self.create_runtime()
self.assertFalse(runtime.service(self.mock_block, 'badging'))

@patch.dict(settings.FEATURES, {'ENABLE_OPENBADGES': True})
def test_get_badge_class(self):
runtime = self.create_runtime()
badge_service = runtime.service(self.mock_block, 'badging')
premade_badge_class = BadgeClassFactory.create()
# Ignore additional parameters. This class already exists.
# We should get back the first class we created, rather than a new one.
badge_class = badge_service.get_badge_class(
slug='test_slug', issuing_component='test_component', description='Attempted override',
criteria='test', display_name='Testola', image_file_handle=get_image('good')
)
# These defaults are set on the factory.
self.assertEqual(badge_class.criteria, 'https://example.com/syllabus')
self.assertEqual(badge_class.display_name, 'Test Badge')
self.assertEqual(badge_class.description, "Yay! It's a test badge.")
# File name won't always be the same.
self.assertEqual(badge_class.image.path, premade_badge_class.image.path)