Retrofit programs data mixin with factories
This should make it easier to test programs-related features. Preface to ECOM-3200.
This commit is contained in:
@@ -4,67 +4,16 @@ Tools to create programs-related data for use in bok choy tests.
|
||||
from collections import namedtuple
|
||||
import json
|
||||
|
||||
import factory
|
||||
import requests
|
||||
|
||||
from . import PROGRAMS_STUB_URL
|
||||
from .config import ConfigModelFixture
|
||||
from openedx.core.djangoapps.programs.tests import factories
|
||||
|
||||
|
||||
FakeProgram = namedtuple('FakeProgram', ['name', 'status', 'org_key', 'course_id'])
|
||||
|
||||
|
||||
class Program(factory.Factory):
|
||||
"""
|
||||
Factory for stubbing program resources from the Programs API (v1).
|
||||
"""
|
||||
class Meta(object):
|
||||
model = dict
|
||||
|
||||
id = factory.Sequence(lambda n: n) # pylint: disable=invalid-name
|
||||
name = 'dummy-program-name'
|
||||
subtitle = 'dummy-program-subtitle'
|
||||
category = 'xseries'
|
||||
status = 'unpublished'
|
||||
marketing_slug = factory.Sequence(lambda n: 'slug-{}'.format(n)) # pylint: disable=unnecessary-lambda
|
||||
organizations = []
|
||||
course_codes = []
|
||||
banner_image_urls = {}
|
||||
|
||||
|
||||
class Organization(factory.Factory):
|
||||
"""
|
||||
Factory for stubbing nested organization resources from the Programs API (v1).
|
||||
"""
|
||||
class Meta(object):
|
||||
model = dict
|
||||
|
||||
key = 'dummyX'
|
||||
display_name = 'dummy-org-display-name'
|
||||
|
||||
|
||||
class CourseCode(factory.Factory):
|
||||
"""
|
||||
Factory for stubbing nested course code resources from the Programs API (v1).
|
||||
"""
|
||||
class Meta(object):
|
||||
model = dict
|
||||
|
||||
display_name = 'dummy-org-display-name'
|
||||
run_modes = []
|
||||
|
||||
|
||||
class RunMode(factory.Factory):
|
||||
"""
|
||||
Factory for stubbing nested run mode resources from the Programs API (v1).
|
||||
"""
|
||||
class Meta(object):
|
||||
model = dict
|
||||
|
||||
course_key = 'org/course/run'
|
||||
mode_slug = 'verified'
|
||||
|
||||
|
||||
class ProgramsFixture(object):
|
||||
"""
|
||||
Interface to set up mock responses from the Programs stub server.
|
||||
@@ -78,11 +27,11 @@ class ProgramsFixture(object):
|
||||
"""
|
||||
programs = []
|
||||
for program in fake_programs:
|
||||
run_mode = RunMode(course_key=program.course_id)
|
||||
course_code = CourseCode(run_modes=[run_mode])
|
||||
org = Organization(key=program.org_key)
|
||||
run_mode = factories.RunMode(course_key=program.course_id)
|
||||
course_code = factories.CourseCode(run_modes=[run_mode])
|
||||
org = factories.Organization(key=program.org_key)
|
||||
|
||||
program = Program(
|
||||
program = factories.Program(
|
||||
name=program.name,
|
||||
status=program.status,
|
||||
organizations=[org],
|
||||
|
||||
@@ -8,7 +8,6 @@ from urlparse import urljoin
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.test import override_settings
|
||||
from edx_oauth2_provider.tests.factories import ClientFactory
|
||||
from opaque_keys.edx import locator
|
||||
@@ -39,6 +38,7 @@ class TestProgramListing(
|
||||
Unit tests for getting the list of programs enrolled by a logged in user
|
||||
"""
|
||||
PASSWORD = 'test'
|
||||
url = reverse('program_listing_view')
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
@@ -81,10 +81,9 @@ class TestProgramListing(
|
||||
"""
|
||||
self.mock_programs_api()
|
||||
self.client.login(username=self.student.username, password=self.PASSWORD)
|
||||
response = self.client.get(reverse("program_listing_view"))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
response = self.client.get(self.url)
|
||||
x_series_url = urljoin(settings.MKTG_URLS.get('ROOT'), 'xseries')
|
||||
self.assertIn(x_series_url, response.content)
|
||||
self.assertContains(response, x_series_url)
|
||||
return response
|
||||
|
||||
def _get_program_checklist(self, program_id):
|
||||
@@ -101,18 +100,18 @@ class TestProgramListing(
|
||||
def test_get_program_with_no_enrollment(self):
|
||||
response = self._setup_and_get_program()
|
||||
for program_element in self._get_program_checklist(0):
|
||||
self.assertNotIn(program_element, response.content)
|
||||
self.assertNotContains(response, program_element)
|
||||
for program_element in self._get_program_checklist(1):
|
||||
self.assertNotIn(program_element, response.content)
|
||||
self.assertNotContains(response, program_element)
|
||||
|
||||
@httpretty.activate
|
||||
def test_get_one_program(self):
|
||||
self._create_course_and_enroll(self.student, *self.COURSE_KEYS[0].split('/'))
|
||||
response = self._setup_and_get_program()
|
||||
for program_element in self._get_program_checklist(0):
|
||||
self.assertIn(program_element, response.content)
|
||||
self.assertContains(response, program_element)
|
||||
for program_element in self._get_program_checklist(1):
|
||||
self.assertNotIn(program_element, response.content)
|
||||
self.assertNotContains(response, program_element)
|
||||
|
||||
@httpretty.activate
|
||||
def test_get_both_program(self):
|
||||
@@ -120,32 +119,34 @@ class TestProgramListing(
|
||||
self._create_course_and_enroll(self.student, *self.COURSE_KEYS[5].split('/'))
|
||||
response = self._setup_and_get_program()
|
||||
for program_element in self._get_program_checklist(0):
|
||||
self.assertIn(program_element, response.content)
|
||||
self.assertContains(response, program_element)
|
||||
for program_element in self._get_program_checklist(1):
|
||||
self.assertIn(program_element, response.content)
|
||||
self.assertContains(response, program_element)
|
||||
|
||||
def test_get_programs_dashboard_not_enabled(self):
|
||||
self.create_programs_config(program_listing_enabled=False)
|
||||
self.client.login(username=self.student.username, password=self.PASSWORD)
|
||||
response = self.client.get(reverse("program_listing_view"))
|
||||
response = self.client.get(self.url)
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
def test_xseries_advertise_disabled(self):
|
||||
self.create_programs_config(program_listing_enabled=True, xseries_ad_enabled=False)
|
||||
self.client.login(username=self.student.username, password=self.PASSWORD)
|
||||
response = self.client.get(reverse("program_listing_view"))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
response = self.client.get(self.url)
|
||||
x_series_url = urljoin(settings.MKTG_URLS.get('ROOT'), 'xseries')
|
||||
self.assertNotIn(x_series_url, response.content)
|
||||
self.assertNotContains(response, x_series_url)
|
||||
|
||||
def test_get_programs_not_logged_in(self):
|
||||
self.create_programs_config()
|
||||
response = self.client.get(reverse("program_listing_view"))
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertIsInstance(response, HttpResponseRedirect)
|
||||
self.assertIn('login', response.url) # pylint: disable=no-member
|
||||
response = self.client.get(self.url)
|
||||
|
||||
def _expected_credetials_data(self):
|
||||
self.assertRedirects(
|
||||
response,
|
||||
'{}?next={}'.format(reverse('signin_user'), self.url)
|
||||
)
|
||||
|
||||
# TODO: Use a factory to generate this data.
|
||||
def _expected_credentials_data(self):
|
||||
""" Dry method for getting expected credentials."""
|
||||
|
||||
return [
|
||||
@@ -172,13 +173,11 @@ class TestProgramListing(
|
||||
self.mock_credentials_api(self.student, data=self.CREDENTIALS_API_RESPONSE, reset_url=False)
|
||||
|
||||
response = self.client.get(reverse("program_listing_view"))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
for certificate in self._expected_credentials_data():
|
||||
self.assertContains(response, certificate['display_name'])
|
||||
self.assertContains(response, certificate['credential_url'])
|
||||
|
||||
for certificate in self._expected_credetials_data():
|
||||
self.assertIn(certificate['display_name'], response.content)
|
||||
self.assertIn(certificate['credential_url'], response.content)
|
||||
|
||||
self.assertIn('images/xseries-certificate-visual.png', response.content)
|
||||
self.assertContains(response, 'images/xseries-certificate-visual.png')
|
||||
|
||||
@httpretty.activate
|
||||
def test_get_xseries_certificates_without_data(self):
|
||||
@@ -193,8 +192,6 @@ class TestProgramListing(
|
||||
self.mock_credentials_api(self.student, data={"results": []}, reset_url=False)
|
||||
|
||||
response = self.client.get(reverse("program_listing_view"))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
for certificate in self._expected_credetials_data():
|
||||
self.assertNotIn(certificate['display_name'], response.content)
|
||||
self.assertNotIn(certificate['credential_url'], response.content)
|
||||
for certificate in self._expected_credentials_data():
|
||||
self.assertNotContains(response, certificate['display_name'])
|
||||
self.assertNotContains(response, certificate['credential_url'])
|
||||
|
||||
54
openedx/core/djangoapps/programs/tests/factories.py
Normal file
54
openedx/core/djangoapps/programs/tests/factories.py
Normal file
@@ -0,0 +1,54 @@
|
||||
"""Factories for generating fake program-related data."""
|
||||
import factory
|
||||
from factory.fuzzy import FuzzyText
|
||||
|
||||
|
||||
class Program(factory.Factory):
|
||||
"""
|
||||
Factory for stubbing program resources from the Programs API (v1).
|
||||
"""
|
||||
class Meta(object):
|
||||
model = dict
|
||||
|
||||
id = factory.Sequence(lambda n: n) # pylint: disable=invalid-name
|
||||
name = FuzzyText(prefix='Program ')
|
||||
subtitle = FuzzyText(prefix='Subtitle ')
|
||||
category = 'xseries'
|
||||
status = 'unpublished'
|
||||
marketing_slug = FuzzyText(prefix='slug_')
|
||||
organizations = []
|
||||
course_codes = []
|
||||
banner_image_urls = {}
|
||||
|
||||
|
||||
class Organization(factory.Factory):
|
||||
"""
|
||||
Factory for stubbing nested organization resources from the Programs API (v1).
|
||||
"""
|
||||
class Meta(object):
|
||||
model = dict
|
||||
|
||||
key = FuzzyText(prefix='org_')
|
||||
display_name = FuzzyText(prefix='Display Name ')
|
||||
|
||||
|
||||
class CourseCode(factory.Factory):
|
||||
"""
|
||||
Factory for stubbing nested course code resources from the Programs API (v1).
|
||||
"""
|
||||
class Meta(object):
|
||||
model = dict
|
||||
|
||||
display_name = FuzzyText(prefix='Display Name ')
|
||||
run_modes = []
|
||||
|
||||
|
||||
class RunMode(factory.Factory):
|
||||
"""
|
||||
Factory for stubbing nested run mode resources from the Programs API (v1).
|
||||
"""
|
||||
class Meta(object):
|
||||
model = dict
|
||||
|
||||
course_key = FuzzyText(prefix='org/', suffix='/run')
|
||||
mode_slug = 'verified'
|
||||
@@ -4,6 +4,7 @@ import json
|
||||
import httpretty
|
||||
|
||||
from openedx.core.djangoapps.programs.models import ProgramsApiConfig
|
||||
from openedx.core.djangoapps.programs.tests import factories
|
||||
|
||||
|
||||
class ProgramsApiConfigMixin(object):
|
||||
@@ -50,176 +51,48 @@ class ProgramsDataMixin(object):
|
||||
'organization-b/course-d/winter',
|
||||
]
|
||||
|
||||
# TODO: Use factory-boy.
|
||||
PROGRAMS_API_RESPONSE = {
|
||||
'results': [
|
||||
{
|
||||
'id': 1,
|
||||
'name': PROGRAM_NAMES[0],
|
||||
'subtitle': 'A program used for testing purposes',
|
||||
'category': 'xseries',
|
||||
'status': 'unpublished',
|
||||
'marketing_slug': '{}_test_url'.format(PROGRAM_NAMES[0].replace(' ', '_')),
|
||||
'organizations': [
|
||||
{
|
||||
'display_name': 'Test Organization A',
|
||||
'key': 'organization-a'
|
||||
}
|
||||
],
|
||||
'course_codes': [
|
||||
{
|
||||
'display_name': 'Test Course A',
|
||||
'key': 'course-a',
|
||||
'organization': {
|
||||
'display_name': 'Test Organization A',
|
||||
'key': 'organization-a'
|
||||
},
|
||||
'run_modes': [
|
||||
{
|
||||
'course_key': COURSE_KEYS[0],
|
||||
'mode_slug': 'verified',
|
||||
'sku': '',
|
||||
'start_date': '2015-11-05T07:39:02.791741Z',
|
||||
'run_key': 'fall'
|
||||
},
|
||||
{
|
||||
'course_key': COURSE_KEYS[1],
|
||||
'mode_slug': 'verified',
|
||||
'sku': '',
|
||||
'start_date': '2015-11-05T07:39:02.791741Z',
|
||||
'run_key': 'winter'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'display_name': 'Test Course B',
|
||||
'key': 'course-b',
|
||||
'organization': {
|
||||
'display_name': 'Test Organization A',
|
||||
'key': 'organization-a'
|
||||
},
|
||||
'run_modes': [
|
||||
{
|
||||
'course_key': COURSE_KEYS[2],
|
||||
'mode_slug': 'verified',
|
||||
'sku': '',
|
||||
'start_date': '2015-11-05T07:39:02.791741Z',
|
||||
'run_key': 'fall'
|
||||
},
|
||||
{
|
||||
'course_key': COURSE_KEYS[3],
|
||||
'mode_slug': 'verified',
|
||||
'sku': '',
|
||||
'start_date': '2015-11-05T07:39:02.791741Z',
|
||||
'run_key': 'winter'
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
'created': '2015-10-26T17:52:32.861000Z',
|
||||
'modified': '2015-11-18T22:21:30.826365Z'
|
||||
},
|
||||
{
|
||||
'id': 2,
|
||||
'name': PROGRAM_NAMES[1],
|
||||
'subtitle': 'Another program used for testing purposes',
|
||||
'category': 'xseries',
|
||||
'status': 'unpublished',
|
||||
'marketing_slug': '{}_test_url'.format(PROGRAM_NAMES[1].replace(' ', '_')),
|
||||
'organizations': [
|
||||
{
|
||||
'display_name': 'Test Organization B',
|
||||
'key': 'organization-b'
|
||||
}
|
||||
],
|
||||
'course_codes': [
|
||||
{
|
||||
'display_name': 'Test Course C',
|
||||
'key': 'course-c',
|
||||
'organization': {
|
||||
'display_name': 'Test Organization B',
|
||||
'key': 'organization-b'
|
||||
},
|
||||
'run_modes': [
|
||||
{
|
||||
'course_key': COURSE_KEYS[4],
|
||||
'mode_slug': 'verified',
|
||||
'sku': '',
|
||||
'start_date': '2015-11-05T07:39:02.791741Z',
|
||||
'run_key': 'fall'
|
||||
},
|
||||
{
|
||||
'course_key': COURSE_KEYS[5],
|
||||
'mode_slug': 'verified',
|
||||
'sku': '',
|
||||
'start_date': '2015-11-05T07:39:02.791741Z',
|
||||
'run_key': 'winter'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'display_name': 'Test Course D',
|
||||
'key': 'course-d',
|
||||
'organization': {
|
||||
'display_name': 'Test Organization B',
|
||||
'key': 'organization-b'
|
||||
},
|
||||
'run_modes': [
|
||||
{
|
||||
'course_key': COURSE_KEYS[6],
|
||||
'mode_slug': 'verified',
|
||||
'sku': '',
|
||||
'start_date': '2015-11-05T07:39:02.791741Z',
|
||||
'run_key': 'fall'
|
||||
},
|
||||
{
|
||||
'course_key': COURSE_KEYS[7],
|
||||
'mode_slug': 'verified',
|
||||
'sku': '',
|
||||
'start_date': '2015-11-05T07:39:02.791741Z',
|
||||
'run_key': 'winter'
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
'created': '2015-10-26T19:59:03.064000Z',
|
||||
'modified': '2015-10-26T19:59:18.536000Z'
|
||||
},
|
||||
{
|
||||
'id': 3,
|
||||
'name': PROGRAM_NAMES[2],
|
||||
'subtitle': 'A third program used for testing purposes',
|
||||
'category': 'xseries',
|
||||
'status': 'unpublished',
|
||||
'marketing_slug': '{}_test_url'.format(PROGRAM_NAMES[2].replace(' ', '_')),
|
||||
'organizations': [
|
||||
{
|
||||
'display_name': 'Test Organization B',
|
||||
'key': 'organization-b'
|
||||
}
|
||||
],
|
||||
'course_codes': [
|
||||
{
|
||||
'display_name': 'Test Course D',
|
||||
'key': 'course-d',
|
||||
'organization': {
|
||||
'display_name': 'Test Organization B',
|
||||
'key': 'organization-b'
|
||||
},
|
||||
'run_modes': [
|
||||
{
|
||||
'course_key': COURSE_KEYS[7],
|
||||
'mode_slug': 'verified',
|
||||
'sku': '',
|
||||
'start_date': '2015-11-05T07:39:02.791741Z',
|
||||
'run_key': 'winter'
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
'created': '2015-10-26T19:59:03.064000Z',
|
||||
'modified': '2015-10-26T19:59:18.536000Z'
|
||||
}
|
||||
factories.Program(
|
||||
id=1,
|
||||
name=PROGRAM_NAMES[0],
|
||||
organizations=[factories.Organization()],
|
||||
course_codes=[
|
||||
factories.CourseCode(run_modes=[
|
||||
factories.RunMode(course_key=COURSE_KEYS[0]),
|
||||
factories.RunMode(course_key=COURSE_KEYS[1]),
|
||||
]),
|
||||
factories.CourseCode(run_modes=[
|
||||
factories.RunMode(course_key=COURSE_KEYS[2]),
|
||||
factories.RunMode(course_key=COURSE_KEYS[3]),
|
||||
]),
|
||||
]
|
||||
),
|
||||
factories.Program(
|
||||
id=2,
|
||||
name=PROGRAM_NAMES[1],
|
||||
organizations=[factories.Organization()],
|
||||
course_codes=[
|
||||
factories.CourseCode(run_modes=[
|
||||
factories.RunMode(course_key=COURSE_KEYS[4]),
|
||||
factories.RunMode(course_key=COURSE_KEYS[5]),
|
||||
]),
|
||||
factories.CourseCode(run_modes=[
|
||||
factories.RunMode(course_key=COURSE_KEYS[6]),
|
||||
factories.RunMode(course_key=COURSE_KEYS[7]),
|
||||
]),
|
||||
]
|
||||
),
|
||||
factories.Program(
|
||||
id=3,
|
||||
name=PROGRAM_NAMES[2],
|
||||
organizations=[factories.Organization()],
|
||||
course_codes=[
|
||||
factories.CourseCode(run_modes=[
|
||||
factories.RunMode(course_key=COURSE_KEYS[7]),
|
||||
]),
|
||||
]
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user