Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
fd6af6b0
Commit
fd6af6b0
authored
Sep 05, 2017
by
Jeremy Bowman
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
ddt usage cleanup
parent
01e41243
Hide whitespace changes
Inline
Side-by-side
Showing
28 changed files
with
431 additions
and
212 deletions
+431
-212
common/djangoapps/course_modes/tests/test_models.py
+8
-2
common/djangoapps/student/roles.py
+5
-1
common/djangoapps/student/tests/test_verification_status.py
+22
-17
common/djangoapps/student/tests/test_views.py
+2
-3
common/djangoapps/util/tests/test_db.py
+12
-6
common/test/utils.py
+27
-0
lms/djangoapps/certificates/tests/test_queue.py
+7
-6
lms/djangoapps/commerce/api/v1/tests/test_views.py
+8
-2
lms/djangoapps/courseware/tests/test_access.py
+11
-6
lms/djangoapps/courseware/tests/test_courses.py
+13
-5
lms/djangoapps/courseware/tests/test_module_render.py
+3
-3
lms/djangoapps/courseware/tests/test_video_handlers.py
+3
-0
lms/djangoapps/courseware/tests/test_video_mongo.py
+14
-8
lms/djangoapps/courseware/tests/test_views.py
+22
-9
lms/djangoapps/grades/tests/test_scores.py
+32
-0
lms/djangoapps/grades/tests/test_signals.py
+53
-32
lms/djangoapps/instructor_task/tests/test_api.py
+20
-11
lms/djangoapps/mobile_api/users/tests.py
+12
-7
lms/djangoapps/teams/tests/test_models.py
+20
-17
lms/djangoapps/verify_student/tests/test_views.py
+12
-4
openedx/core/djangoapps/content/block_structure/tests/helpers.py
+3
-0
openedx/core/djangoapps/content/course_overviews/tests/test_course_overviews.py
+19
-6
openedx/core/djangoapps/credit/tests/test_signals.py
+12
-6
openedx/core/djangoapps/programs/tests/test_backpopulate_program_credentials.py
+55
-42
openedx/core/djangoapps/user_api/validation/tests/test_views.py
+10
-10
openedx/core/djangoapps/util/tests/test_user_messages.py
+6
-4
openedx/core/lib/xblock_builtin/xblock_discussion/tests.py
+9
-0
openedx/features/course_experience/tests/views/test_course_outline.py
+11
-5
No files found.
common/djangoapps/course_modes/tests/test_models.py
View file @
fd6af6b0
...
...
@@ -29,6 +29,11 @@ class CourseModeModelTest(TestCase):
"""
Tests for the CourseMode model
"""
NOW
=
'now'
DATES
=
{
NOW
:
datetime
.
now
(),
None
:
None
,
}
def
setUp
(
self
):
super
(
CourseModeModelTest
,
self
)
.
setUp
()
...
...
@@ -317,10 +322,11 @@ class CourseModeModelTest(TestCase):
CourseMode
.
PROFESSIONAL
,
CourseMode
.
NO_ID_PROFESSIONAL_MODE
),
(
datetime
.
now
()
,
None
),
(
NOW
,
None
),
))
@ddt.unpack
def
test_invalid_mode_expiration
(
self
,
mode_slug
,
exp_dt
):
def
test_invalid_mode_expiration
(
self
,
mode_slug
,
exp_dt_name
):
exp_dt
=
self
.
DATES
[
exp_dt_name
]
is_error_expected
=
CourseMode
.
is_professional_slug
(
mode_slug
)
and
exp_dt
is
not
None
try
:
self
.
create_mode
(
mode_slug
=
mode_slug
,
mode_name
=
mode_slug
.
title
(),
expiration_datetime
=
exp_dt
,
min_price
=
10
)
...
...
common/djangoapps/student/roles.py
View file @
fd6af6b0
...
...
@@ -234,12 +234,16 @@ class CourseRole(RoleBase):
def
course_group_already_exists
(
self
,
course_key
):
return
CourseAccessRole
.
objects
.
filter
(
org
=
course_key
.
org
,
course_id
=
course_key
)
.
exists
()
def
__repr__
(
self
):
return
'<{}: course_key={}>'
.
format
(
self
.
__class__
.
__name__
,
self
.
course_key
)
class
OrgRole
(
RoleBase
):
"""
A named role in a particular org independent of course
"""
pass
def
__repr__
(
self
):
return
'<{}>'
.
format
(
self
.
__class__
.
__name__
)
@register_access_role
...
...
common/djangoapps/student/tests/test_verification_status.py
View file @
fd6af6b0
...
...
@@ -33,8 +33,13 @@ from xmodule.modulestore.tests.factories import CourseFactory
class
TestCourseVerificationStatus
(
UrlResetMixin
,
ModuleStoreTestCase
):
"""Tests for per-course verification status on the dashboard. """
PAST
=
datetime
.
now
(
UTC
)
-
timedelta
(
days
=
5
)
FUTURE
=
datetime
.
now
(
UTC
)
+
timedelta
(
days
=
5
)
PAST
=
'past'
FUTURE
=
'future'
DATES
=
{
PAST
:
datetime
.
now
(
UTC
)
-
timedelta
(
days
=
5
),
FUTURE
:
datetime
.
now
(
UTC
)
+
timedelta
(
days
=
5
),
None
:
None
,
}
URLCONF_MODULES
=
[
'verify_student.urls'
]
...
...
@@ -91,14 +96,14 @@ class TestCourseVerificationStatus(UrlResetMixin, ModuleStoreTestCase):
self
.
_assert_course_verification_status
(
VERIFY_STATUS_NEED_TO_VERIFY
)
def
test_need_to_verify_expiration
(
self
):
self
.
_setup_mode_and_enrollment
(
self
.
FUTURE
,
"verified"
)
self
.
_setup_mode_and_enrollment
(
self
.
DATES
[
self
.
FUTURE
]
,
"verified"
)
response
=
self
.
client
.
get
(
self
.
dashboard_url
)
self
.
assertContains
(
response
,
self
.
BANNER_ALT_MESSAGES
[
VERIFY_STATUS_NEED_TO_VERIFY
])
self
.
assertContains
(
response
,
"You only have 4 days left to verify for this course."
)
@ddt.data
(
None
,
FUTURE
)
def
test_waiting_approval
(
self
,
expiration
):
self
.
_setup_mode_and_enrollment
(
expiration
,
"verified"
)
self
.
_setup_mode_and_enrollment
(
self
.
DATES
[
expiration
]
,
"verified"
)
# The student has submitted a photo verification
attempt
=
SoftwareSecurePhotoVerification
.
objects
.
create
(
user
=
self
.
user
)
...
...
@@ -110,7 +115,7 @@ class TestCourseVerificationStatus(UrlResetMixin, ModuleStoreTestCase):
@ddt.data
(
None
,
FUTURE
)
def
test_fully_verified
(
self
,
expiration
):
self
.
_setup_mode_and_enrollment
(
expiration
,
"verified"
)
self
.
_setup_mode_and_enrollment
(
self
.
DATES
[
expiration
]
,
"verified"
)
# The student has an approved verification
attempt
=
SoftwareSecurePhotoVerification
.
objects
.
create
(
user
=
self
.
user
)
...
...
@@ -127,7 +132,7 @@ class TestCourseVerificationStatus(UrlResetMixin, ModuleStoreTestCase):
def
test_missed_verification_deadline
(
self
):
# Expiration date in the past
self
.
_setup_mode_and_enrollment
(
self
.
PAST
,
"verified"
)
self
.
_setup_mode_and_enrollment
(
self
.
DATES
[
self
.
PAST
]
,
"verified"
)
# The student does NOT have an approved verification
# so the status should show that the student missed the deadline.
...
...
@@ -135,7 +140,7 @@ class TestCourseVerificationStatus(UrlResetMixin, ModuleStoreTestCase):
def
test_missed_verification_deadline_verification_was_expired
(
self
):
# Expiration date in the past
self
.
_setup_mode_and_enrollment
(
self
.
PAST
,
"verified"
)
self
.
_setup_mode_and_enrollment
(
self
.
DATES
[
self
.
PAST
]
,
"verified"
)
# Create a verification, but the expiration date of the verification
# occurred before the deadline.
...
...
@@ -143,7 +148,7 @@ class TestCourseVerificationStatus(UrlResetMixin, ModuleStoreTestCase):
attempt
.
mark_ready
()
attempt
.
submit
()
attempt
.
approve
()
attempt
.
created_at
=
self
.
PAST
-
timedelta
(
days
=
900
)
attempt
.
created_at
=
self
.
DATES
[
self
.
PAST
]
-
timedelta
(
days
=
900
)
attempt
.
save
()
# The student didn't have an approved verification at the deadline,
...
...
@@ -152,14 +157,14 @@ class TestCourseVerificationStatus(UrlResetMixin, ModuleStoreTestCase):
def
test_missed_verification_deadline_but_later_verified
(
self
):
# Expiration date in the past
self
.
_setup_mode_and_enrollment
(
self
.
PAST
,
"verified"
)
self
.
_setup_mode_and_enrollment
(
self
.
DATES
[
self
.
PAST
]
,
"verified"
)
# Successfully verify, but after the deadline has already passed
attempt
=
SoftwareSecurePhotoVerification
.
objects
.
create
(
user
=
self
.
user
)
attempt
.
mark_ready
()
attempt
.
submit
()
attempt
.
approve
()
attempt
.
created_at
=
self
.
PAST
-
timedelta
(
days
=
900
)
attempt
.
created_at
=
self
.
DATES
[
self
.
PAST
]
-
timedelta
(
days
=
900
)
attempt
.
save
()
# The student didn't have an approved verification at the deadline,
...
...
@@ -168,7 +173,7 @@ class TestCourseVerificationStatus(UrlResetMixin, ModuleStoreTestCase):
def
test_verification_denied
(
self
):
# Expiration date in the future
self
.
_setup_mode_and_enrollment
(
self
.
FUTURE
,
"verified"
)
self
.
_setup_mode_and_enrollment
(
self
.
DATES
[
self
.
FUTURE
]
,
"verified"
)
# Create a verification with the specified status
attempt
=
SoftwareSecurePhotoVerification
.
objects
.
create
(
user
=
self
.
user
)
...
...
@@ -182,7 +187,7 @@ class TestCourseVerificationStatus(UrlResetMixin, ModuleStoreTestCase):
def
test_verification_error
(
self
):
# Expiration date in the future
self
.
_setup_mode_and_enrollment
(
self
.
FUTURE
,
"verified"
)
self
.
_setup_mode_and_enrollment
(
self
.
DATES
[
self
.
FUTURE
]
,
"verified"
)
# Create a verification with the specified status
attempt
=
SoftwareSecurePhotoVerification
.
objects
.
create
(
user
=
self
.
user
)
...
...
@@ -196,7 +201,7 @@ class TestCourseVerificationStatus(UrlResetMixin, ModuleStoreTestCase):
@override_settings
(
VERIFY_STUDENT
=
{
"DAYS_GOOD_FOR"
:
5
,
"EXPIRING_SOON_WINDOW"
:
10
})
def
test_verification_will_expire_by_deadline
(
self
):
# Expiration date in the future
self
.
_setup_mode_and_enrollment
(
self
.
FUTURE
,
"verified"
)
self
.
_setup_mode_and_enrollment
(
self
.
DATES
[
self
.
FUTURE
]
,
"verified"
)
# Create a verification attempt that:
# 1) Is current (submitted in the last year)
...
...
@@ -213,7 +218,7 @@ class TestCourseVerificationStatus(UrlResetMixin, ModuleStoreTestCase):
@override_settings
(
VERIFY_STUDENT
=
{
"DAYS_GOOD_FOR"
:
5
,
"EXPIRING_SOON_WINDOW"
:
10
})
def
test_reverification_submitted_with_current_approved_verificaiton
(
self
):
# Expiration date in the future
self
.
_setup_mode_and_enrollment
(
self
.
FUTURE
,
"verified"
)
self
.
_setup_mode_and_enrollment
(
self
.
DATES
[
self
.
FUTURE
]
,
"verified"
)
# Create a verification attempt that is approved but expiring soon
attempt
=
SoftwareSecurePhotoVerification
.
objects
.
create
(
user
=
self
.
user
)
...
...
@@ -236,7 +241,7 @@ class TestCourseVerificationStatus(UrlResetMixin, ModuleStoreTestCase):
def
test_verification_occurred_after_deadline
(
self
):
# Expiration date in the past
self
.
_setup_mode_and_enrollment
(
self
.
PAST
,
"verified"
)
self
.
_setup_mode_and_enrollment
(
self
.
DATES
[
self
.
PAST
]
,
"verified"
)
# The deadline has passed, and we've asked the student
# to reverify (through the support team).
...
...
@@ -250,7 +255,7 @@ class TestCourseVerificationStatus(UrlResetMixin, ModuleStoreTestCase):
def
test_with_two_verifications
(
self
):
# checking if a user has two verification and but most recent verification course deadline is expired
self
.
_setup_mode_and_enrollment
(
self
.
FUTURE
,
"verified"
)
self
.
_setup_mode_and_enrollment
(
self
.
DATES
[
self
.
FUTURE
]
,
"verified"
)
# The student has an approved verification
attempt
=
SoftwareSecurePhotoVerification
.
objects
.
create
(
user
=
self
.
user
)
...
...
@@ -274,7 +279,7 @@ class TestCourseVerificationStatus(UrlResetMixin, ModuleStoreTestCase):
CourseModeFactory
.
create
(
course_id
=
course2
.
id
,
mode_slug
=
"verified"
,
expiration_datetime
=
self
.
PAST
expiration_datetime
=
self
.
DATES
[
self
.
PAST
]
)
CourseEnrollmentFactory
(
course_id
=
course2
.
id
,
...
...
common/djangoapps/student/tests/test_views.py
View file @
fd6af6b0
...
...
@@ -297,19 +297,18 @@ class StudentDashboardTests(SharedModuleStoreTestCase):
@patch.multiple
(
'django.conf.settings'
,
**
MOCK_SETTINGS
)
@ddt.data
(
*
itertools
.
product
(
[
TOMORROW
],
[
True
,
False
],
[
True
,
False
],
[
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
],
)
)
@ddt.unpack
def
test_sharing_icons_for_future_course
(
self
,
s
tart_date
,
s
et_marketing
,
set_social_sharing
,
modulestore_type
):
def
test_sharing_icons_for_future_course
(
self
,
set_marketing
,
set_social_sharing
,
modulestore_type
):
"""
Verify that the course sharing icons show up if course is starting in future and
any of marketing or social sharing urls are set.
"""
self
.
course
=
CourseFactory
.
create
(
start
=
s
tart_date
,
emit_signals
=
True
,
default_store
=
modulestore_type
)
self
.
course
=
CourseFactory
.
create
(
start
=
s
elf
.
TOMORROW
,
emit_signals
=
True
,
default_store
=
modulestore_type
)
self
.
course_enrollment
=
CourseEnrollmentFactory
(
course_id
=
self
.
course
.
id
,
user
=
self
.
user
)
self
.
set_course_sharing_urls
(
set_marketing
,
set_social_sharing
)
...
...
common/djangoapps/util/tests/test_db.py
View file @
fd6af6b0
...
...
@@ -31,20 +31,26 @@ class TransactionManagersTestCase(TransactionTestCase):
To test do: "./manage.py lms --settings=test_with_mysql test util.tests.test_db"
"""
DECORATORS
=
{
'outer_atomic'
:
outer_atomic
(),
'outer_atomic_read_committed'
:
outer_atomic
(
read_committed
=
True
),
'commit_on_success'
:
commit_on_success
(),
'commit_on_success_read_committed'
:
commit_on_success
(
read_committed
=
True
),
}
@ddt.data
(
(
outer_atomic
()
,
IntegrityError
,
None
,
True
),
(
outer_atomic
(
read_committed
=
True
)
,
type
(
None
),
False
,
True
),
(
commit_on_success
()
,
IntegrityError
,
None
,
True
),
(
commit_on_success
(
read_committed
=
True
)
,
type
(
None
),
False
,
True
),
(
'outer_atomic'
,
IntegrityError
,
None
,
True
),
(
'outer_atomic_read_committed'
,
type
(
None
),
False
,
True
),
(
'commit_on_success'
,
IntegrityError
,
None
,
True
),
(
'commit_on_success_read_committed'
,
type
(
None
),
False
,
True
),
)
@ddt.unpack
def
test_concurrent_requests
(
self
,
transaction_decorator
,
exception_class
,
created_in_1
,
created_in_2
):
def
test_concurrent_requests
(
self
,
transaction_decorator
_name
,
exception_class
,
created_in_1
,
created_in_2
):
"""
Test that when isolation level is set to READ COMMITTED get_or_create()
for the same row in concurrent requests does not raise an IntegrityError.
"""
transaction_decorator
=
self
.
DECORATORS
[
transaction_decorator_name
]
if
connection
.
vendor
!=
'mysql'
:
raise
unittest
.
SkipTest
(
'Only works on MySQL.'
)
...
...
common/test/utils.py
View file @
fd6af6b0
"""
General testing utilities.
"""
import
functools
import
sys
from
contextlib
import
contextmanager
...
...
@@ -124,3 +125,29 @@ class MockS3Mixin(object):
def
tearDown
(
self
):
self
.
_mock_s3
.
stop
()
super
(
MockS3Mixin
,
self
)
.
tearDown
()
class
reprwrapper
(
object
):
"""
Wrapper class for functions that need a normalized string representation.
"""
def
__init__
(
self
,
func
):
self
.
_func
=
func
self
.
repr
=
'Func: {}'
.
format
(
func
.
__name__
)
functools
.
update_wrapper
(
self
,
func
)
def
__call__
(
self
,
*
args
,
**
kw
):
return
self
.
_func
(
*
args
,
**
kw
)
def
__repr__
(
self
):
return
self
.
repr
def
normalize_repr
(
func
):
"""
Function decorator used to normalize its string representation.
Used to wrap functions used as ddt parameters, so pytest-xdist
doesn't complain about the sequence of discovered tests differing
between worker processes.
"""
return
reprwrapper
(
func
)
lms/djangoapps/certificates/tests/test_queue.py
View file @
fd6af6b0
...
...
@@ -207,42 +207,42 @@ class XQueueCertInterfaceAddCertificateTest(ModuleStoreTestCase):
# Eligible and should stay that way
(
CertificateStatuses
.
downloadable
,
datetime
.
now
(
pytz
.
UTC
)
-
timedelta
(
days
=
2
),
timedelta
(
days
=-
2
),
'Pass'
,
CertificateStatuses
.
generating
),
# Ensure that certs in the wrong state can be fixed by regeneration
(
CertificateStatuses
.
downloadable
,
datetime
.
now
(
pytz
.
UTC
)
-
timedelta
(
hours
=
1
),
timedelta
(
hours
=-
1
),
'Pass'
,
CertificateStatuses
.
audit_passing
),
# Ineligible and should stay that way
(
CertificateStatuses
.
audit_passing
,
datetime
.
now
(
pytz
.
UTC
)
-
timedelta
(
hours
=
1
),
timedelta
(
hours
=-
1
),
'Pass'
,
CertificateStatuses
.
audit_passing
),
# As above
(
CertificateStatuses
.
audit_notpassing
,
datetime
.
now
(
pytz
.
UTC
)
-
timedelta
(
hours
=
1
),
timedelta
(
hours
=-
1
),
'Pass'
,
CertificateStatuses
.
audit_passing
),
# As above
(
CertificateStatuses
.
audit_notpassing
,
datetime
.
now
(
pytz
.
UTC
)
-
timedelta
(
hours
=
1
),
timedelta
(
hours
=-
1
),
None
,
CertificateStatuses
.
audit_notpassing
),
)
@ddt.unpack
@override_settings
(
AUDIT_CERT_CUTOFF_DATE
=
datetime
.
now
(
pytz
.
UTC
)
-
timedelta
(
days
=
1
))
def
test_regen_audit_certs_eligibility
(
self
,
status
,
created_d
ate
,
grade
,
expected_status
):
def
test_regen_audit_certs_eligibility
(
self
,
status
,
created_d
elta
,
grade
,
expected_status
):
"""
Test that existing audit certificates remain eligible even if cert
generation is re-run.
...
...
@@ -254,6 +254,7 @@ class XQueueCertInterfaceAddCertificateTest(ModuleStoreTestCase):
is_active
=
True
,
mode
=
CourseMode
.
AUDIT
,
)
created_date
=
datetime
.
now
(
pytz
.
UTC
)
+
created_delta
with
freezegun
.
freeze_time
(
created_date
):
GeneratedCertificateFactory
(
user
=
self
.
user_2
,
...
...
lms/djangoapps/commerce/api/v1/tests/test_views.py
View file @
fd6af6b0
...
...
@@ -106,6 +106,11 @@ class CourseListViewTests(CourseApiViewTestMixin, ModuleStoreTestCase):
@ddt.ddt
class
CourseRetrieveUpdateViewTests
(
CourseApiViewTestMixin
,
ModuleStoreTestCase
):
""" Tests for CourseRetrieveUpdateView. """
NOW
=
'now'
DATES
=
{
NOW
:
datetime
.
now
(),
None
:
None
,
}
def
setUp
(
self
):
super
(
CourseRetrieveUpdateViewTests
,
self
)
.
setUp
()
...
...
@@ -276,12 +281,13 @@ class CourseRetrieveUpdateViewTests(CourseApiViewTestMixin, ModuleStoreTestCase)
@ddt.data
(
*
itertools
.
product
(
(
'honor'
,
'audit'
,
'verified'
,
'professional'
,
'no-id-professional'
),
(
datetime
.
now
()
,
None
),
(
NOW
,
None
),
))
@ddt.unpack
def
test_update_professional_expiration
(
self
,
mode_slug
,
expiration_datetime
):
def
test_update_professional_expiration
(
self
,
mode_slug
,
expiration_datetime
_name
):
""" Verify that pushing a mode with a professional certificate and an expiration datetime
will be rejected (this is not allowed). """
expiration_datetime
=
self
.
DATES
[
expiration_datetime_name
]
mode
=
self
.
_serialize_course_mode
(
CourseMode
(
mode_slug
=
mode_slug
,
...
...
lms/djangoapps/courseware/tests/test_access.py
View file @
fd6af6b0
...
...
@@ -162,9 +162,14 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase, MilestonesTes
"""
Tests for the various access controls on the student dashboard
"""
TOMORROW
=
datetime
.
datetime
.
now
(
pytz
.
utc
)
+
datetime
.
timedelta
(
days
=
1
)
YESTERDAY
=
datetime
.
datetime
.
now
(
pytz
.
utc
)
-
datetime
.
timedelta
(
days
=
1
)
TOMORROW
=
'tomorrow'
YESTERDAY
=
'yesterday'
MODULESTORE
=
TEST_DATA_SPLIT_MODULESTORE
DATES
=
{
TOMORROW
:
datetime
.
datetime
.
now
(
pytz
.
utc
)
+
datetime
.
timedelta
(
days
=
1
),
YESTERDAY
:
datetime
.
datetime
.
now
(
pytz
.
utc
)
-
datetime
.
timedelta
(
days
=
1
),
None
:
None
,
}
def
setUp
(
self
):
super
(
AccessTestCase
,
self
)
.
setUp
()
...
...
@@ -439,7 +444,7 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase, MilestonesTes
mock_unit
=
Mock
(
location
=
self
.
course
.
location
,
user_partitions
=
[])
mock_unit
.
_class_tags
=
{}
# Needed for detached check in _has_access_descriptor
mock_unit
.
visible_to_staff_only
=
visible_to_staff_only
mock_unit
.
start
=
s
tart
mock_unit
.
start
=
s
elf
.
DATES
[
start
]
mock_unit
.
merged_group_access
=
{}
self
.
verify_access
(
mock_unit
,
expected_access
,
expected_error_type
)
...
...
@@ -448,7 +453,7 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase, MilestonesTes
mock_unit
=
Mock
(
user_partitions
=
[])
mock_unit
.
_class_tags
=
{}
mock_unit
.
days_early_for_beta
=
2
mock_unit
.
start
=
self
.
TOMORROW
mock_unit
.
start
=
self
.
DATES
[
self
.
TOMORROW
]
mock_unit
.
visible_to_staff_only
=
False
mock_unit
.
merged_group_access
=
{}
...
...
@@ -465,7 +470,7 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase, MilestonesTes
mock_unit
=
Mock
(
location
=
self
.
course
.
location
,
user_partitions
=
[])
mock_unit
.
_class_tags
=
{}
# Needed for detached check in _has_access_descriptor
mock_unit
.
visible_to_staff_only
=
False
mock_unit
.
start
=
s
tart
mock_unit
.
start
=
s
elf
.
DATES
[
start
]
mock_unit
.
merged_group_access
=
{}
self
.
verify_access
(
mock_unit
,
True
)
...
...
@@ -486,7 +491,7 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase, MilestonesTes
mock_unit
=
Mock
(
location
=
self
.
course
.
location
,
user_partitions
=
[])
mock_unit
.
_class_tags
=
{}
# Needed for detached check in _has_access_descriptor
mock_unit
.
visible_to_staff_only
=
False
mock_unit
.
start
=
s
tart
mock_unit
.
start
=
s
elf
.
DATES
[
start
]
mock_unit
.
merged_group_access
=
{}
self
.
verify_access
(
mock_unit
,
expected_access
,
expected_error_type
)
...
...
lms/djangoapps/courseware/tests/test_courses.py
View file @
fd6af6b0
...
...
@@ -49,6 +49,12 @@ TEST_DATA_DIR = settings.COMMON_TEST_DATA_ROOT
class
CoursesTest
(
ModuleStoreTestCase
):
"""Test methods related to fetching courses."""
ENABLED_SIGNALS
=
[
'course_published'
]
GET_COURSE_WITH_ACCESS
=
'get_course_with_access'
GET_COURSE_OVERVIEW_WITH_ACCESS
=
'get_course_overview_with_access'
COURSE_ACCESS_FUNCS
=
{
GET_COURSE_WITH_ACCESS
:
get_course_with_access
,
GET_COURSE_OVERVIEW_WITH_ACCESS
:
get_course_overview_with_access
,
}
@override_settings
(
CMS_BASE
=
CMS_BASE_TEST
)
def
test_get_cms_course_block_link
(
self
):
...
...
@@ -64,8 +70,9 @@ class CoursesTest(ModuleStoreTestCase):
cms_url
=
u"//{}/course/{}"
.
format
(
CMS_BASE_TEST
,
unicode
(
self
.
course
.
location
))
self
.
assertEqual
(
cms_url
,
get_cms_block_link
(
self
.
course
,
'course'
))
@ddt.data
(
get_course_with_access
,
get_course_overview_with_access
)
def
test_get_course_func_with_access_error
(
self
,
course_access_func
):
@ddt.data
(
GET_COURSE_WITH_ACCESS
,
GET_COURSE_OVERVIEW_WITH_ACCESS
)
def
test_get_course_func_with_access_error
(
self
,
course_access_func_name
):
course_access_func
=
self
.
COURSE_ACCESS_FUNCS
[
course_access_func_name
]
user
=
UserFactory
.
create
()
course
=
CourseFactory
.
create
(
visible_to_staff_only
=
True
)
...
...
@@ -76,11 +83,12 @@ class CoursesTest(ModuleStoreTestCase):
self
.
assertFalse
(
error
.
exception
.
access_response
.
has_access
)
@ddt.data
(
(
get_course_with_access
,
1
),
(
get_course_overview_with_access
,
0
),
(
GET_COURSE_WITH_ACCESS
,
1
),
(
GET_COURSE_OVERVIEW_WITH_ACCESS
,
0
),
)
@ddt.unpack
def
test_get_course_func_with_access
(
self
,
course_access_func
,
num_mongo_calls
):
def
test_get_course_func_with_access
(
self
,
course_access_func_name
,
num_mongo_calls
):
course_access_func
=
self
.
COURSE_ACCESS_FUNCS
[
course_access_func_name
]
user
=
UserFactory
.
create
()
course
=
CourseFactory
.
create
(
emit_signals
=
True
)
with
check_mongo_calls
(
num_mongo_calls
):
...
...
lms/djangoapps/courseware/tests/test_module_render.py
View file @
fd6af6b0
...
...
@@ -1570,11 +1570,11 @@ class TestStaffDebugInfo(SharedModuleStoreTestCase):
PER_COURSE_ANONYMIZED_DESCRIPTORS
=
(
LTIDescriptor
,
)
# The "set" here is to work around the bug that load_classes returns duplicates for multiply-de
lc
ared classes.
PER_STUDENT_ANONYMIZED_DESCRIPTORS
=
set
(
# The "set" here is to work around the bug that load_classes returns duplicates for multiply-de
cl
ared classes.
PER_STUDENT_ANONYMIZED_DESCRIPTORS
=
s
orted
(
s
et
(
class_
for
(
name
,
class_
)
in
XModuleDescriptor
.
load_classes
()
if
not
issubclass
(
class_
,
PER_COURSE_ANONYMIZED_DESCRIPTORS
)
)
)
,
key
=
str
)
@attr
(
shard
=
1
)
...
...
lms/djangoapps/courseware/tests/test_video_handlers.py
View file @
fd6af6b0
...
...
@@ -13,6 +13,7 @@ from mock import MagicMock, Mock, patch
from
nose.plugins.attrib
import
attr
from
webob
import
Request
from
common.test.utils
import
normalize_repr
from
openedx.core.djangoapps.contentserver.caching
import
del_cached_content
from
xmodule.contentstore.content
import
StaticContent
from
xmodule.contentstore.django
import
contentstore
...
...
@@ -105,6 +106,7 @@ def _upload_file(subs_file, location, filename):
del_cached_content
(
content
.
location
)
@normalize_repr
def
attach_sub
(
item
,
filename
):
"""
Attach `en` transcript.
...
...
@@ -112,6 +114,7 @@ def attach_sub(item, filename):
item
.
sub
=
filename
@normalize_repr
def
attach_bumper_transcript
(
item
,
filename
,
lang
=
"en"
):
"""
Attach bumper transcript.
...
...
lms/djangoapps/courseware/tests/test_video_mongo.py
View file @
fd6af6b0
...
...
@@ -17,6 +17,7 @@ from path import Path as path
from
xmodule.contentstore.content
import
StaticContent
from
xmodule.exceptions
import
NotFoundError
from
xmodule.modulestore
import
ModuleStoreEnum
from
xmodule.modulestore.inheritance
import
own_metadata
from
xmodule.modulestore.tests.django_utils
import
TEST_DATA_MONGO_MODULESTORE
,
TEST_DATA_SPLIT_MODULESTORE
from
xmodule.tests.test_import
import
DummySystem
...
...
@@ -29,6 +30,11 @@ from .helpers import BaseTestXmodule
from
.test_video_handlers
import
TestVideo
from
.test_video_xml
import
SOURCE_XML
MODULESTORES
=
{
ModuleStoreEnum
.
Type
.
mongo
:
TEST_DATA_MONGO_MODULESTORE
,
ModuleStoreEnum
.
Type
.
split
:
TEST_DATA_SPLIT_MODULESTORE
,
}
@attr
(
shard
=
1
)
class
TestVideoYouTube
(
TestVideo
):
...
...
@@ -1162,14 +1168,14 @@ class TestEditorSavedMethod(BaseTestXmodule):
self
.
test_dir
=
path
(
__file__
)
.
abspath
()
.
dirname
()
.
dirname
()
.
dirname
()
.
dirname
()
.
dirname
()
self
.
file_path
=
self
.
test_dir
+
'/common/test/data/uploads/'
+
self
.
file_name
@ddt.data
(
TEST_DATA_MONGO_MODULESTORE
,
TEST_DATA_SPLIT_MODULESTORE
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
)
def
test_editor_saved_when_html5_sub_not_exist
(
self
,
default_store
):
"""
When there is youtube_sub exist but no html5_sub present for
html5_sources, editor_saved function will generate new html5_sub
for video.
"""
self
.
MODULESTORE
=
default_store
# pylint: disable=invalid-name
self
.
MODULESTORE
=
MODULESTORES
[
default_store
]
# pylint: disable=invalid-name
self
.
initialize_module
(
metadata
=
self
.
metadata
)
item
=
self
.
store
.
get_item
(
self
.
item_descriptor
.
location
)
with
open
(
self
.
file_path
,
"r"
)
as
myfile
:
...
...
@@ -1184,13 +1190,13 @@ class TestEditorSavedMethod(BaseTestXmodule):
self
.
assertIsInstance
(
Transcript
.
get_asset
(
item
.
location
,
'subs_3_yD_cEKoCk.srt.sjson'
),
StaticContent
)
self
.
assertIsInstance
(
Transcript
.
get_asset
(
item
.
location
,
'subs_video.srt.sjson'
),
StaticContent
)
@ddt.data
(
TEST_DATA_MONGO_MODULESTORE
,
TEST_DATA_SPLIT_MODULESTORE
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
)
def
test_editor_saved_when_youtube_and_html5_subs_exist
(
self
,
default_store
):
"""
When both youtube_sub and html5_sub already exist then no new
sub will be generated by editor_saved function.
"""
self
.
MODULESTORE
=
default_store
self
.
MODULESTORE
=
MODULESTORES
[
default_store
]
self
.
initialize_module
(
metadata
=
self
.
metadata
)
item
=
self
.
store
.
get_item
(
self
.
item_descriptor
.
location
)
with
open
(
self
.
file_path
,
"r"
)
as
myfile
:
...
...
@@ -1205,12 +1211,12 @@ class TestEditorSavedMethod(BaseTestXmodule):
item
.
editor_saved
(
self
.
user
,
old_metadata
,
None
)
self
.
assertFalse
(
manage_video_subtitles_save
.
called
)
@ddt.data
(
TEST_DATA_MONGO_MODULESTORE
,
TEST_DATA_SPLIT_MODULESTORE
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
)
def
test_editor_saved_with_unstripped_video_id
(
self
,
default_store
):
"""
Verify editor saved when video id contains spaces/tabs.
"""
self
.
MODULESTORE
=
default_store
self
.
MODULESTORE
=
MODULESTORES
[
default_store
]
stripped_video_id
=
unicode
(
uuid4
())
unstripped_video_id
=
u'{video_id}{tabs}'
.
format
(
video_id
=
stripped_video_id
,
tabs
=
u'
\t\t\t
'
)
self
.
metadata
.
update
({
...
...
@@ -1226,14 +1232,14 @@ class TestEditorSavedMethod(BaseTestXmodule):
item
.
editor_saved
(
self
.
user
,
old_metadata
,
None
)
self
.
assertEqual
(
item
.
edx_video_id
,
stripped_video_id
)
@ddt.data
(
TEST_DATA_MONGO_MODULESTORE
,
TEST_DATA_SPLIT_MODULESTORE
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
)
@patch
(
'xmodule.video_module.video_module.edxval_api.get_url_for_profile'
,
Mock
(
return_value
=
'test_yt_id'
))
def
test_editor_saved_with_yt_val_profile
(
self
,
default_store
):
"""
Verify editor saved overrides `youtube_id_1_0` when a youtube val profile is there
for a given `edx_video_id`.
"""
self
.
MODULESTORE
=
default_store
self
.
MODULESTORE
=
MODULESTORES
[
default_store
]
self
.
initialize_module
(
metadata
=
self
.
metadata
)
item
=
self
.
store
.
get_item
(
self
.
item_descriptor
.
location
)
self
.
assertEqual
(
item
.
youtube_id_1_0
,
'3_yD_cEKoCk'
)
...
...
lms/djangoapps/courseware/tests/test_views.py
View file @
fd6af6b0
...
...
@@ -251,6 +251,11 @@ class ViewsTestCase(ModuleStoreTestCase):
"""
Tests for views.py methods.
"""
YESTERDAY
=
'yesterday'
DATES
=
{
YESTERDAY
:
datetime
.
now
(
UTC
)
-
timedelta
(
days
=
1
),
None
:
None
,
}
def
setUp
(
self
):
super
(
ViewsTestCase
,
self
)
.
setUp
()
...
...
@@ -751,7 +756,7 @@ class ViewsTestCase(ModuleStoreTestCase):
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertIn
(
'Financial Assistance Application'
,
response
.
content
)
@ddt.data
(([
CourseMode
.
AUDIT
,
CourseMode
.
VERIFIED
],
CourseMode
.
AUDIT
,
True
,
datetime
.
now
(
UTC
)
-
timedelta
(
days
=
1
)
),
@ddt.data
(([
CourseMode
.
AUDIT
,
CourseMode
.
VERIFIED
],
CourseMode
.
AUDIT
,
True
,
YESTERDAY
),
([
CourseMode
.
AUDIT
,
CourseMode
.
VERIFIED
],
CourseMode
.
VERIFIED
,
True
,
None
),
([
CourseMode
.
AUDIT
,
CourseMode
.
VERIFIED
],
CourseMode
.
AUDIT
,
False
,
None
),
([
CourseMode
.
AUDIT
],
CourseMode
.
AUDIT
,
False
,
None
))
...
...
@@ -770,7 +775,7 @@ class ViewsTestCase(ModuleStoreTestCase):
# Create Course Modes
for
mode
in
course_modes
:
CourseModeFactory
.
create
(
mode_slug
=
mode
,
course_id
=
course
.
id
,
expiration_datetime
=
expiration
)
CourseModeFactory
.
create
(
mode_slug
=
mode
,
course_id
=
course
.
id
,
expiration_datetime
=
self
.
DATES
[
expiration
]
)
# Enroll user in the course
CourseEnrollmentFactory
(
course_id
=
course
.
id
,
user
=
self
.
user
,
mode
=
enrollment_mode
)
...
...
@@ -1705,10 +1710,16 @@ class ProgressPageShowCorrectnessTests(ProgressPageBaseTests):
# Constants used in the test data
NOW
=
datetime
.
now
(
UTC
)
DAY_DELTA
=
timedelta
(
days
=
1
)
YESTERDAY
=
NOW
-
DAY_DELTA
TODAY
=
NOW
TOMORROW
=
NOW
+
DAY_DELTA
YESTERDAY
=
'yesterday'
TODAY
=
'today'
TOMORROW
=
'tomorrow'
GRADER_TYPE
=
'Homework'
DATES
=
{
YESTERDAY
:
NOW
-
DAY_DELTA
,
TODAY
:
NOW
,
TOMORROW
:
NOW
+
DAY_DELTA
,
None
:
None
,
}
def
setUp
(
self
):
super
(
ProgressPageShowCorrectnessTests
,
self
)
.
setUp
()
...
...
@@ -1853,12 +1864,12 @@ class ProgressPageShowCorrectnessTests(ProgressPageBaseTests):
(
ShowCorrectness
.
PAST_DUE
,
TOMORROW
,
True
),
)
@ddt.unpack
def
test_progress_page_no_problem_scores
(
self
,
show_correctness
,
due_date
,
graded
):
def
test_progress_page_no_problem_scores
(
self
,
show_correctness
,
due_date
_name
,
graded
):
"""
Test that "no problem scores are present" for a course with no problems,
regardless of the various show correctness settings.
"""
self
.
setup_course
(
show_correctness
=
show_correctness
,
due_date
=
due_date
,
graded
=
graded
)
self
.
setup_course
(
show_correctness
=
show_correctness
,
due_date
=
self
.
DATES
[
due_date_name
]
,
graded
=
graded
)
resp
=
self
.
_get_progress_page
()
# Test that no problem scores are present
...
...
@@ -1893,11 +1904,12 @@ class ProgressPageShowCorrectnessTests(ProgressPageBaseTests):
(
ShowCorrectness
.
PAST_DUE
,
TOMORROW
,
True
,
False
),
)
@ddt.unpack
def
test_progress_page_hide_scores_from_learner
(
self
,
show_correctness
,
due_date
,
graded
,
show_grades
):
def
test_progress_page_hide_scores_from_learner
(
self
,
show_correctness
,
due_date
_name
,
graded
,
show_grades
):
"""
Test that problem scores are hidden on progress page when correctness is not available to the learner, and that
they are visible when it is.
"""
due_date
=
self
.
DATES
[
due_date_name
]
self
.
setup_course
(
show_correctness
=
show_correctness
,
due_date
=
due_date
,
graded
=
graded
)
self
.
add_problem
()
...
...
@@ -1944,10 +1956,11 @@ class ProgressPageShowCorrectnessTests(ProgressPageBaseTests):
(
ShowCorrectness
.
PAST_DUE
,
TOMORROW
,
True
,
True
),
)
@ddt.unpack
def
test_progress_page_hide_scores_from_staff
(
self
,
show_correctness
,
due_date
,
graded
,
show_grades
):
def
test_progress_page_hide_scores_from_staff
(
self
,
show_correctness
,
due_date
_name
,
graded
,
show_grades
):
"""
Test that problem scores are hidden from staff viewing a learner's progress page only if show_correctness=never.
"""
due_date
=
self
.
DATES
[
due_date_name
]
self
.
setup_course
(
show_correctness
=
show_correctness
,
due_date
=
due_date
,
graded
=
graded
)
self
.
add_problem
()
...
...
lms/djangoapps/grades/tests/test_scores.py
View file @
fd6af6b0
...
...
@@ -19,6 +19,35 @@ from xmodule.graders import ProblemScore
NOW
=
now
()
def
submission_value_repr
(
self
):
"""
String representation for the SubmissionValue namedtuple which excludes
the "created_at" attribute that changes with each execution. Needed for
consistency of ddt-generated test methods across pytest-xdist workers.
"""
return
'<SubmissionValue exists={}>'
.
format
(
self
.
exists
)
def
csm_value_repr
(
self
):
"""
String representation for the CSMValue namedtuple which excludes
the "created" attribute that changes with each execution. Needed for
consistency of ddt-generated test methods across pytest-xdist workers.
"""
return
'<CSMValue exists={} raw_earned={}>'
.
format
(
self
.
exists
,
self
.
raw_earned
)
def
expected_result_repr
(
self
):
"""
String representation for the ExpectedResult namedtuple which excludes
the "first_attempted" attribute that changes with each execution. Needed
for consistency of ddt-generated test methods across pytest-xdist workers.
"""
included
=
(
'raw_earned'
,
'raw_possible'
,
'weighted_earned'
,
'weighted_possible'
,
'weight'
,
'graded'
)
attributes
=
[
'{}={}'
.
format
(
name
,
getattr
(
self
,
name
))
for
name
in
included
]
return
'<ExpectedResult {}>'
.
format
(
' '
.
join
(
attributes
))
class
TestScoredBlockTypes
(
TestCase
):
"""
Tests for the possibly_scored function.
...
...
@@ -52,13 +81,16 @@ class TestGetScore(TestCase):
location
=
'test_location'
SubmissionValue
=
namedtuple
(
'SubmissionValue'
,
'exists, points_earned, points_possible, created_at'
)
SubmissionValue
.
__repr__
=
submission_value_repr
CSMValue
=
namedtuple
(
'CSMValue'
,
'exists, raw_earned, raw_possible, created'
)
CSMValue
.
__repr__
=
csm_value_repr
PersistedBlockValue
=
namedtuple
(
'PersistedBlockValue'
,
'exists, raw_possible, weight, graded'
)
ContentBlockValue
=
namedtuple
(
'ContentBlockValue'
,
'raw_possible, weight, explicit_graded'
)
ExpectedResult
=
namedtuple
(
'ExpectedResult'
,
'raw_earned, raw_possible, weighted_earned, weighted_possible, weight, graded, first_attempted'
)
ExpectedResult
.
__repr__
=
expected_result_repr
def
_create_submissions_scores
(
self
,
submission_value
):
"""
...
...
lms/djangoapps/grades/tests/test_signals.py
View file @
fd6af6b0
...
...
@@ -16,7 +16,6 @@ from util.date_utils import to_timestamp
from
..constants
import
ScoreDatabaseTableEnum
from
..signals.handlers
import
(
disconnect_submissions_signal_receiver
,
enqueue_subsection_update
,
problem_raw_score_changed_handler
,
submissions_score_reset_handler
,
submissions_score_set_handler
...
...
@@ -28,20 +27,30 @@ UUID_REGEX = re.compile(ur'%(hex)s{8}-%(hex)s{4}-%(hex)s{4}-%(hex)s{4}-%(hex)s{1
FROZEN_NOW_DATETIME
=
datetime
.
now
()
.
replace
(
tzinfo
=
pytz
.
UTC
)
FROZEN_NOW_TIMESTAMP
=
to_timestamp
(
FROZEN_NOW_DATETIME
)
SUBMISSION_SET_KWARGS
=
{
'points_possible'
:
10
,
'points_earned'
:
5
,
'anonymous_user_id'
:
'anonymous_id'
,
'course_id'
:
'CourseID'
,
'item_id'
:
'i4x://org/course/usage/123456'
,
'created_at'
:
FROZEN_NOW_TIMESTAMP
,
SUBMISSIONS_SCORE_SET_HANDLER
=
'submissions_score_set_handler'
SUBMISSIONS_SCORE_RESET_HANDLER
=
'submissions_score_reset_handler'
HANDLERS
=
{
SUBMISSIONS_SCORE_SET_HANDLER
:
submissions_score_set_handler
,
SUBMISSIONS_SCORE_RESET_HANDLER
:
submissions_score_reset_handler
,
}
SUBMISSION_RESET_KWARGS
=
{
'anonymous_user_id'
:
'anonymous_id'
,
'course_id'
:
'CourseID'
,
'item_id'
:
'i4x://org/course/usage/123456'
,
'created_at'
:
FROZEN_NOW_TIMESTAMP
,
SUBMISSION_SET_KWARGS
=
'submission_set_kwargs'
SUBMISSION_RESET_KWARGS
=
'submission_reset_kwargs'
SUBMISSION_KWARGS
=
{
SUBMISSION_SET_KWARGS
:
{
'points_possible'
:
10
,
'points_earned'
:
5
,
'anonymous_user_id'
:
'anonymous_id'
,
'course_id'
:
'CourseID'
,
'item_id'
:
'i4x://org/course/usage/123456'
,
'created_at'
:
FROZEN_NOW_TIMESTAMP
,
},
SUBMISSION_RESET_KWARGS
:
{
'anonymous_user_id'
:
'anonymous_id'
,
'course_id'
:
'CourseID'
,
'item_id'
:
'i4x://org/course/usage/123456'
,
'created_at'
:
FROZEN_NOW_TIMESTAMP
,
},
}
PROBLEM_RAW_SCORE_CHANGED_KWARGS
=
{
...
...
@@ -82,6 +91,10 @@ class ScoreChangedSignalRelayTest(TestCase):
This ensures that listeners in the LMS only have to handle one type
of signal for all scoring events regardless of their origin.
"""
SIGNALS
=
{
'score_set'
:
score_set
,
'score_reset'
:
score_reset
,
}
def
setUp
(
self
):
"""
...
...
@@ -110,11 +123,11 @@ class ScoreChangedSignalRelayTest(TestCase):
return
mock
@ddt.data
(
[
submissions_score_set_handler
,
SUBMISSION_SET_KWARGS
,
5
,
10
],
[
submissions_score_reset_handler
,
SUBMISSION_RESET_KWARGS
,
0
,
0
],
[
SUBMISSIONS_SCORE_SET_HANDLER
,
SUBMISSION_SET_KWARGS
,
5
,
10
],
[
SUBMISSIONS_SCORE_RESET_HANDLER
,
SUBMISSION_RESET_KWARGS
,
0
,
0
],
)
@ddt.unpack
def
test_score_set_signal_handler
(
self
,
handler
,
kwargs
,
earned
,
possible
):
def
test_score_set_signal_handler
(
self
,
handler
_name
,
kwargs
,
earned
,
possible
):
"""
Ensure that on receipt of a score_(re)set signal from the Submissions API,
the signal handler correctly converts it to a PROBLEM_WEIGHTED_SCORE_CHANGED
...
...
@@ -122,7 +135,9 @@ class ScoreChangedSignalRelayTest(TestCase):
Also ensures that the handler calls user_by_anonymous_id correctly.
"""
handler
(
None
,
**
kwargs
)
local_kwargs
=
SUBMISSION_KWARGS
[
kwargs
]
.
copy
()
handler
=
HANDLERS
[
handler_name
]
handler
(
None
,
**
local_kwargs
)
expected_set_kwargs
=
{
'sender'
:
None
,
'weighted_possible'
:
possible
,
...
...
@@ -134,35 +149,36 @@ class ScoreChangedSignalRelayTest(TestCase):
'modified'
:
FROZEN_NOW_TIMESTAMP
,
'score_db_table'
:
'submissions'
,
}
if
handler
==
submissions_score_reset_handler
:
if
kwargs
==
SUBMISSION_RESET_KWARGS
:
expected_set_kwargs
[
'score_deleted'
]
=
True
self
.
signal_mock
.
assert_called_once_with
(
**
expected_set_kwargs
)
self
.
get_user_mock
.
assert_called_once_with
(
kwargs
[
'anonymous_user_id'
])
self
.
get_user_mock
.
assert_called_once_with
(
local_
kwargs
[
'anonymous_user_id'
])
def
test_tnl_6599_zero_possible_bug
(
self
):
"""
Ensure that, if coming from the submissions API, signals indicating a
a possible score of 0 are swallowed for reasons outlined in TNL-6559.
"""
local_kwargs
=
SUBMISSION_
SET_KWARGS
.
copy
()
local_kwargs
=
SUBMISSION_
KWARGS
[
SUBMISSION_SET_KWARGS
]
.
copy
()
local_kwargs
[
'points_earned'
]
=
0
local_kwargs
[
'points_possible'
]
=
0
submissions_score_set_handler
(
None
,
**
local_kwargs
)
self
.
signal_mock
.
assert_not_called
()
@ddt.data
(
[
submissions_score_set_handler
,
SUBMISSION_SET_KWARGS
],
[
submissions_score_reset_handler
,
SUBMISSION_RESET_KWARGS
]
[
SUBMISSIONS_SCORE_SET_HANDLER
,
SUBMISSION_SET_KWARGS
],
[
SUBMISSIONS_SCORE_RESET_HANDLER
,
SUBMISSION_RESET_KWARGS
]
)
@ddt.unpack
def
test_score_set_missing_kwarg
(
self
,
handler
,
kwargs
):
def
test_score_set_missing_kwarg
(
self
,
handler
_name
,
kwargs
):
"""
Ensure that, on receipt of a score_(re)set signal from the Submissions API
that does not have the correct kwargs, the courseware model does not
generate a signal.
"""
for
missing
in
kwargs
:
local_kwargs
=
kwargs
.
copy
()
handler
=
HANDLERS
[
handler_name
]
for
missing
in
SUBMISSION_KWARGS
[
kwargs
]:
local_kwargs
=
SUBMISSION_KWARGS
[
kwargs
]
.
copy
()
del
local_kwargs
[
missing
]
with
self
.
assertRaises
(
KeyError
):
...
...
@@ -170,18 +186,19 @@ class ScoreChangedSignalRelayTest(TestCase):
self
.
signal_mock
.
assert_not_called
()
@ddt.data
(
[
submissions_score_set_handler
,
SUBMISSION_SET_KWARGS
],
[
submissions_score_reset_handler
,
SUBMISSION_RESET_KWARGS
]
[
SUBMISSIONS_SCORE_SET_HANDLER
,
SUBMISSION_SET_KWARGS
],
[
SUBMISSIONS_SCORE_RESET_HANDLER
,
SUBMISSION_RESET_KWARGS
]
)
@ddt.unpack
def
test_score_set_bad_user
(
self
,
handler
,
kwargs
):
def
test_score_set_bad_user
(
self
,
handler
_name
,
kwargs
):
"""
Ensure that, on receipt of a score_(re)set signal from the Submissions API
that has an invalid user ID, the courseware model does not generate a
signal.
"""
handler
=
HANDLERS
[
handler_name
]
self
.
get_user_mock
=
self
.
setup_patch
(
'lms.djangoapps.grades.signals.handlers.user_by_anonymous_id'
,
None
)
handler
(
None
,
**
kwargs
)
handler
(
None
,
**
SUBMISSION_KWARGS
[
kwargs
]
)
self
.
signal_mock
.
assert_not_called
()
def
test_raw_score_changed_signal_handler
(
self
):
...
...
@@ -198,14 +215,18 @@ class ScoreChangedSignalRelayTest(TestCase):
self
.
signal_mock
.
assert_called_with
(
**
expected_set_kwargs
)
@ddt.data
(
[
score_set
,
'lms.djangoapps.grades.signals.handlers.submissions_score_set_handler'
,
SUBMISSION_SET_KWARGS
],
[
score_reset
,
'lms.djangoapps.grades.signals.handlers.submissions_score_reset_handler'
,
SUBMISSION_RESET_KWARGS
]
[
'score_set'
,
'lms.djangoapps.grades.signals.handlers.submissions_score_set_handler'
,
SUBMISSION_SET_KWARGS
],
[
'score_reset'
,
'lms.djangoapps.grades.signals.handlers.submissions_score_reset_handler'
,
SUBMISSION_RESET_KWARGS
]
)
@ddt.unpack
def
test_disconnect_manager
(
self
,
signal
,
handler
,
kwargs
):
def
test_disconnect_manager
(
self
,
signal
_name
,
handler
,
kwargs
):
"""
Tests to confirm the disconnect_submissions_signal_receiver context manager is working correctly.
"""
signal
=
self
.
SIGNALS
[
signal_name
]
kwargs
=
SUBMISSION_KWARGS
[
kwargs
]
.
copy
()
handler_mock
=
self
.
setup_patch
(
handler
,
None
)
# Receiver connected before we start
...
...
lms/djangoapps/instructor_task/tests/test_api.py
View file @
fd6af6b0
...
...
@@ -7,6 +7,7 @@ from nose.plugins.attrib import attr
from
bulk_email.models
import
SEND_TO_LEARNERS
,
SEND_TO_MYSELF
,
SEND_TO_STAFF
,
CourseEmail
from
certificates.models
import
CertificateGenerationHistory
,
CertificateStatuses
from
common.test.utils
import
normalize_repr
from
courseware.tests.factories
import
UserFactory
from
lms.djangoapps.instructor_task.api
import
(
SpecificStudentIdMissingError
,
...
...
@@ -147,21 +148,29 @@ class InstructorTaskModuleSubmitTest(InstructorTaskModuleTestCase):
self
.
_test_submit_with_long_url
(
submit_delete_problem_state_for_all_students
)
@ddt.data
(
(
submit_rescore_problem_for_all_students
,
'rescore_problem'
),
(
submit_rescore_problem_for_all_students
,
'rescore_problem_if_higher'
,
{
'only_if_higher'
:
True
}),
(
submit_rescore_problem_for_student
,
'rescore_problem'
,
{
'student'
:
True
}),
(
submit_rescore_problem_for_student
,
'rescore_problem_if_higher'
,
{
'student'
:
True
,
'only_if_higher'
:
True
}),
(
submit_reset_problem_attempts_for_all_students
,
'reset_problem_attempts'
),
(
submit_delete_problem_state_for_all_students
,
'delete_problem_state'
),
(
submit_rescore_entrance_exam_for_student
,
'rescore_problem'
,
{
'student'
:
True
}),
(
normalize_repr
(
submit_rescore_problem_for_all_students
),
'rescore_problem'
),
(
submit_rescore_entrance_exam_for_student
,
normalize_repr
(
submit_rescore_problem_for_all_students
),
'rescore_problem_if_higher'
,
{
'only_if_higher'
:
True
}
),
(
normalize_repr
(
submit_rescore_problem_for_student
),
'rescore_problem'
,
{
'student'
:
True
}),
(
normalize_repr
(
submit_rescore_problem_for_student
),
'rescore_problem_if_higher'
,
{
'student'
:
True
,
'only_if_higher'
:
True
}
),
(
normalize_repr
(
submit_reset_problem_attempts_for_all_students
),
'reset_problem_attempts'
),
(
normalize_repr
(
submit_delete_problem_state_for_all_students
),
'delete_problem_state'
),
(
normalize_repr
(
submit_rescore_entrance_exam_for_student
),
'rescore_problem'
,
{
'student'
:
True
}),
(
normalize_repr
(
submit_rescore_entrance_exam_for_student
),
'rescore_problem_if_higher'
,
{
'student'
:
True
,
'only_if_higher'
:
True
},
),
(
submit_reset_problem_attempts_in_entrance_exam
,
'reset_problem_attempts'
,
{
'student'
:
True
}),
(
submit_delete_entrance_exam_state_for_student
,
'delete_problem_state'
,
{
'student'
:
True
}),
(
submit_override_score
,
'override_problem_score'
,
{
'student'
:
True
,
'score'
:
0
})
(
normalize_repr
(
submit_reset_problem_attempts_in_entrance_exam
)
,
'reset_problem_attempts'
,
{
'student'
:
True
}),
(
normalize_repr
(
submit_delete_entrance_exam_state_for_student
)
,
'delete_problem_state'
,
{
'student'
:
True
}),
(
normalize_repr
(
submit_override_score
)
,
'override_problem_score'
,
{
'student'
:
True
,
'score'
:
0
})
)
@ddt.unpack
def
test_submit_task
(
self
,
task_function
,
expected_task_type
,
params
=
None
):
...
...
lms/djangoapps/mobile_api/users/tests.py
View file @
fd6af6b0
...
...
@@ -84,6 +84,11 @@ class TestUserEnrollmentApi(UrlResetMixin, MobileAPITestCase, MobileAuthUserTest
LAST_WEEK
=
datetime
.
datetime
.
now
(
pytz
.
UTC
)
-
datetime
.
timedelta
(
days
=
7
)
ADVERTISED_START
=
"Spring 2016"
ENABLED_SIGNALS
=
[
'course_published'
]
DATES
=
{
'next_week'
:
NEXT_WEEK
,
'last_week'
:
LAST_WEEK
,
'default_start_date'
:
DEFAULT_START_DATE
,
}
@patch.dict
(
settings
.
FEATURES
,
{
"ENABLE_DISCUSSION_SERVICE"
:
True
})
def
setUp
(
self
,
*
args
,
**
kwargs
):
...
...
@@ -175,12 +180,12 @@ class TestUserEnrollmentApi(UrlResetMixin, MobileAPITestCase, MobileAuthUserTest
self
.
assertFalse
(
result
[
'has_access'
])
@ddt.data
(
(
NEXT_WEEK
,
ADVERTISED_START
,
ADVERTISED_START
,
"string"
),
(
NEXT_WEEK
,
None
,
defaultfilters
.
date
(
NEXT_WEEK
,
"DATE_FORMAT"
),
"timestamp"
),
(
NEXT_WEEK
,
''
,
defaultfilters
.
date
(
NEXT_WEEK
,
"DATE_FORMAT"
),
"timestamp"
),
(
DEFAULT_START_DATE
,
ADVERTISED_START
,
ADVERTISED_START
,
"string"
),
(
DEFAULT_START_DATE
,
''
,
None
,
"empty"
),
(
DEFAULT_START_DATE
,
None
,
None
,
"empty"
),
(
'next_week'
,
ADVERTISED_START
,
ADVERTISED_START
,
"string"
),
(
'next_week'
,
None
,
defaultfilters
.
date
(
NEXT_WEEK
,
"DATE_FORMAT"
),
"timestamp"
),
(
'next_week'
,
''
,
defaultfilters
.
date
(
NEXT_WEEK
,
"DATE_FORMAT"
),
"timestamp"
),
(
'default_start_date'
,
ADVERTISED_START
,
ADVERTISED_START
,
"string"
),
(
'default_start_date'
,
''
,
None
,
"empty"
),
(
'default_start_date'
,
None
,
None
,
"empty"
),
)
@ddt.unpack
@patch.dict
(
settings
.
FEATURES
,
{
'DISABLE_START_DATES'
:
False
,
'ENABLE_MKTG_SITE'
:
True
})
...
...
@@ -190,7 +195,7 @@ class TestUserEnrollmentApi(UrlResetMixin, MobileAPITestCase, MobileAuthUserTest
case the course has not started
"""
self
.
login
()
course
=
CourseFactory
.
create
(
start
=
s
tart
,
advertised_start
=
advertised_start
,
mobile_available
=
True
)
course
=
CourseFactory
.
create
(
start
=
s
elf
.
DATES
[
start
]
,
advertised_start
=
advertised_start
,
mobile_available
=
True
)
self
.
enroll
(
course
.
id
)
response
=
self
.
api_response
()
...
...
lms/djangoapps/teams/tests/test_models.py
View file @
fd6af6b0
...
...
@@ -118,17 +118,17 @@ class TeamMembershipTest(SharedModuleStoreTestCase):
class
TeamSignalsTest
(
EventTestMixin
,
SharedModuleStoreTestCase
):
"""Tests for handling of team-related signals."""
SIGNALS
_LIST
=
(
thread_created
,
thread_edited
,
thread_deleted
,
thread_voted
,
comment_created
,
comment_edited
,
comment_deleted
,
comment_voted
,
comment_endorsed
)
SIGNALS
=
{
'thread_created'
:
thread_created
,
'thread_edited'
:
thread_edited
,
'thread_deleted'
:
thread_deleted
,
'thread_voted'
:
thread_voted
,
'comment_created'
:
comment_created
,
'comment_edited'
:
comment_edited
,
'comment_deleted'
:
comment_deleted
,
'comment_voted'
:
comment_voted
,
'comment_endorsed'
:
comment_endorsed
,
}
DISCUSSION_TOPIC_ID
=
'test_topic'
...
...
@@ -180,30 +180,33 @@ class TeamSignalsTest(EventTestMixin, SharedModuleStoreTestCase):
@ddt.data
(
*
itertools
.
product
(
SIGNALS
_LIST
,
SIGNALS
.
keys
()
,
((
'user'
,
True
),
(
'moderator'
,
False
))
)
)
@ddt.unpack
def
test_signals
(
self
,
signal
,
(
user
,
should_update
)):
def
test_signals
(
self
,
signal
_name
,
(
user
,
should_update
)):
"""Test that `last_activity_at` is correctly updated when team-related
signals are sent.
"""
with
self
.
assert_last_activity_updated
(
should_update
):
user
=
getattr
(
self
,
user
)
signal
=
self
.
SIGNALS
[
signal_name
]
signal
.
send
(
sender
=
None
,
user
=
user
,
post
=
self
.
mock_comment
())
@ddt.data
(
thread_voted
,
comment_voted
)
def
test_vote_others_post
(
self
,
signal
):
@ddt.data
(
'thread_voted'
,
'comment_voted'
)
def
test_vote_others_post
(
self
,
signal
_name
):
"""Test that voting on another user's post correctly fires a
signal."""
with
self
.
assert_last_activity_updated
(
True
):
signal
=
self
.
SIGNALS
[
signal_name
]
signal
.
send
(
sender
=
None
,
user
=
self
.
user
,
post
=
self
.
mock_comment
(
user
=
self
.
moderator
))
@ddt.data
(
*
SIGNALS
_LIST
)
def
test_signals_course_context
(
self
,
signal
):
@ddt.data
(
*
SIGNALS
.
keys
()
)
def
test_signals_course_context
(
self
,
signal
_name
):
"""Test that `last_activity_at` is not updated when activity takes
place in discussions outside of a team.
"""
with
self
.
assert_last_activity_updated
(
False
):
signal
=
self
.
SIGNALS
[
signal_name
]
signal
.
send
(
sender
=
None
,
user
=
self
.
user
,
post
=
self
.
mock_comment
(
context
=
'course'
))
lms/djangoapps/verify_student/tests/test_views.py
View file @
fd6af6b0
...
...
@@ -89,8 +89,15 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin):
PASSWORD
=
"test_password"
NOW
=
datetime
.
now
(
pytz
.
UTC
)
YESTERDAY
=
NOW
-
timedelta
(
days
=
1
)
TOMORROW
=
NOW
+
timedelta
(
days
=
1
)
YESTERDAY
=
'yesterday'
TOMORROW
=
'tomorrow'
NEXT_YEAR
=
'next_year'
DATES
=
{
YESTERDAY
:
NOW
-
timedelta
(
days
=
1
),
TOMORROW
:
NOW
+
timedelta
(
days
=
1
),
NEXT_YEAR
:
NOW
+
timedelta
(
days
=
360
),
None
:
None
,
}
URLCONF_MODULES
=
[
'openedx.core.djangoapps.embargo'
]
...
...
@@ -492,7 +499,7 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin):
)
@ddt.unpack
def
test_payment_confirmation_course_details
(
self
,
course_start
,
show_courseware_url
):
course
=
self
.
_create_course
(
"verified"
,
course_start
=
course_start
)
course
=
self
.
_create_course
(
"verified"
,
course_start
=
self
.
DATES
[
course_start
]
)
self
.
_enroll
(
course
.
id
,
"verified"
)
response
=
self
.
_get_page
(
'verify_student_payment_confirmation'
,
course
.
id
)
...
...
@@ -753,9 +760,10 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase, XssTestMixin):
self
.
assertContains
(
response
,
"verification deadline"
)
self
.
assertContains
(
response
,
deadline
)
@ddt.data
(
datetime
.
now
(
tz
=
pytz
.
UTC
)
+
timedelta
(
days
=
360
)
,
None
)
@ddt.data
(
NEXT_YEAR
,
None
)
def
test_course_mode_expired_verification_deadline_in_future
(
self
,
verification_deadline
):
"""Verify that student can not upgrade in expired course mode."""
verification_deadline
=
self
.
DATES
[
verification_deadline
]
course_modes
=
(
"verified"
,
"credit"
)
course
=
self
.
_create_course
(
*
course_modes
)
...
...
openedx/core/djangoapps/content/block_structure/tests/helpers.py
View file @
fd6af6b0
...
...
@@ -183,6 +183,9 @@ class MockTransformer(BlockStructureTransformer):
def
transform
(
self
,
usage_info
,
block_structure
):
pass
def
__repr__
(
self
):
return
self
.
name
()
class
MockFilteringTransformer
(
FilteringTransformerMixin
,
BlockStructureTransformer
):
"""
...
...
openedx/core/djangoapps/content/course_overviews/tests/test_course_overviews.py
View file @
fd6af6b0
...
...
@@ -46,10 +46,18 @@ class CourseOverviewTestCase(ModuleStoreTestCase):
"""
TODAY
=
timezone
.
now
()
LAST_MONTH
=
TODAY
-
datetime
.
timedelta
(
days
=
30
)
LAST_WEEK
=
TODAY
-
datetime
.
timedelta
(
days
=
7
)
NEXT_WEEK
=
TODAY
+
datetime
.
timedelta
(
days
=
7
)
NEXT_MONTH
=
TODAY
+
datetime
.
timedelta
(
days
=
30
)
LAST_MONTH
=
'last_month'
LAST_WEEK
=
'last_week'
NEXT_WEEK
=
'next_week'
NEXT_MONTH
=
'next_month'
DATES
=
{
'default_start_date'
:
DEFAULT_START_DATE
,
LAST_MONTH
:
TODAY
-
datetime
.
timedelta
(
days
=
30
),
LAST_WEEK
:
TODAY
-
datetime
.
timedelta
(
days
=
7
),
NEXT_WEEK
:
TODAY
+
datetime
.
timedelta
(
days
=
7
),
NEXT_MONTH
:
TODAY
+
datetime
.
timedelta
(
days
=
30
),
None
:
None
,
}
COURSE_OVERVIEW_TABS
=
{
'courseware'
,
'info'
,
'textbooks'
,
'discussion'
,
'wiki'
,
'progress'
}
...
...
@@ -229,7 +237,7 @@ class CourseOverviewTestCase(ModuleStoreTestCase):
},
{
# # Don't set display name
"start"
:
DEFAULT_START_DATE
,
# Default start and end dates
"start"
:
'default_start_date'
,
# Default start and end dates
"end"
:
None
,
"advertised_start"
:
None
,
# No advertised start
"pre_requisite_courses"
:
[],
# No pre-requisites
...
...
@@ -251,10 +259,15 @@ class CourseOverviewTestCase(ModuleStoreTestCase):
modulestore_type (ModuleStoreEnum.Type): type of store to create the
course in.
"""
kwargs
=
course_kwargs
.
copy
()
kwargs
[
'start'
]
=
self
.
DATES
[
course_kwargs
[
'start'
]]
kwargs
[
'end'
]
=
self
.
DATES
[
course_kwargs
[
'end'
]]
if
'announcement'
in
course_kwargs
:
kwargs
[
'announcement'
]
=
self
.
DATES
[
course_kwargs
[
'announcement'
]]
# Note: We specify a value for 'run' here because, for some reason,
# .create raises an InvalidKeyError if we don't (even though my
# other test functions don't specify a run but work fine).
course
=
CourseFactory
.
create
(
default_store
=
modulestore_type
,
run
=
"TestRun"
,
**
course_
kwargs
)
course
=
CourseFactory
.
create
(
default_store
=
modulestore_type
,
run
=
"TestRun"
,
**
kwargs
)
self
.
check_course_overview_against_course
(
course
)
@ddt.data
(
ModuleStoreEnum
.
Type
.
mongo
,
ModuleStoreEnum
.
Type
.
split
)
...
...
openedx/core/djangoapps/credit/tests/test_signals.py
View file @
fd6af6b0
...
...
@@ -35,6 +35,12 @@ class TestMinGradedRequirementStatus(ModuleStoreTestCase):
VALID_DUE_DATE
=
datetime
.
now
(
pytz
.
UTC
)
+
timedelta
(
days
=
20
)
EXPIRED_DUE_DATE
=
datetime
.
now
(
pytz
.
UTC
)
-
timedelta
(
days
=
20
)
DATES
=
{
'valid'
:
VALID_DUE_DATE
,
'expired'
:
EXPIRED_DUE_DATE
,
None
:
None
,
}
def
setUp
(
self
):
super
(
TestMinGradedRequirementStatus
,
self
)
.
setUp
()
self
.
course
=
CourseFactory
.
create
(
...
...
@@ -85,13 +91,13 @@ class TestMinGradedRequirementStatus(ModuleStoreTestCase):
self
.
assertEqual
(
req_status
[
0
][
'reason'
],
expected_reason
)
@ddt.data
(
(
0.6
,
VALID_DUE_DATE
),
(
0.6
,
'valid'
),
(
0.52
,
None
),
)
@ddt.unpack
def
test_min_grade_requirement_with_valid_grade
(
self
,
grade
,
due_date
):
def
test_min_grade_requirement_with_valid_grade
(
self
,
grade
,
due_date
_name
):
"""Test with valid grades submitted before deadline"""
self
.
assert_requirement_status
(
grade
,
due_date
,
'satisfied'
)
self
.
assert_requirement_status
(
grade
,
self
.
DATES
[
due_date_name
]
,
'satisfied'
)
def
test_grade_changed
(
self
):
""" Verify successive calls to update a satisfied grade requirement are recorded. """
...
...
@@ -106,12 +112,12 @@ class TestMinGradedRequirementStatus(ModuleStoreTestCase):
@ddt.data
(
(
0.50
,
None
),
(
0.51
,
None
),
(
0.40
,
VALID_DUE_DATE
),
(
0.40
,
'valid'
),
)
@ddt.unpack
def
test_min_grade_requirement_failed_grade_valid_deadline
(
self
,
grade
,
due_date
):
def
test_min_grade_requirement_failed_grade_valid_deadline
(
self
,
grade
,
due_date
_name
):
"""Test with failed grades and deadline is still open or not defined."""
self
.
assert_requirement_status
(
grade
,
due_date
,
None
)
self
.
assert_requirement_status
(
grade
,
self
.
DATES
[
due_date_name
]
,
None
)
def
test_min_grade_requirement_failed_grade_expired_deadline
(
self
):
"""Test with failed grades and deadline expire"""
...
...
openedx/core/djangoapps/programs/tests/test_backpopulate_program_credentials.py
View file @
fd6af6b0
...
...
@@ -29,6 +29,10 @@ COMMAND_MODULE = 'openedx.core.djangoapps.programs.management.commands.backpopul
class
BackpopulateProgramCredentialsTests
(
CatalogIntegrationMixin
,
CredentialsApiConfigMixin
,
TestCase
):
"""Tests for the backpopulate_program_credentials management command."""
course_run_key
,
alternate_course_run_key
=
(
generate_course_run_key
()
for
__
in
range
(
2
))
# Constants for the _get_programs_data hierarchy types used in test_flatten()
SEPARATE_PROGRAMS
=
'separate_programs'
SEPARATE_COURSES
=
'separate_courses'
SAME_COURSE
=
'same_course'
def
setUp
(
self
):
super
(
BackpopulateProgramCredentialsTests
,
self
)
.
setUp
()
...
...
@@ -44,6 +48,54 @@ class BackpopulateProgramCredentialsTests(CatalogIntegrationMixin, CredentialsAp
catalog_integration
=
self
.
create_catalog_integration
()
UserFactory
(
username
=
catalog_integration
.
service_username
)
def
_get_programs_data
(
self
,
hierarchy_type
):
"""
Generate a mock response for get_programs() with the given type of
course hierarchy. Dramatically simplifies (and makes consistent
between test runs) the ddt-generated test_flatten methods.
"""
if
hierarchy_type
==
self
.
SEPARATE_PROGRAMS
:
return
[
ProgramFactory
(
courses
=
[
CourseFactory
(
course_runs
=
[
CourseRunFactory
(
key
=
self
.
course_run_key
),
]),
]
),
ProgramFactory
(
courses
=
[
CourseFactory
(
course_runs
=
[
CourseRunFactory
(
key
=
self
.
alternate_course_run_key
),
]),
]
),
]
elif
hierarchy_type
==
self
.
SEPARATE_COURSES
:
return
[
ProgramFactory
(
courses
=
[
CourseFactory
(
course_runs
=
[
CourseRunFactory
(
key
=
self
.
course_run_key
),
]),
CourseFactory
(
course_runs
=
[
CourseRunFactory
(
key
=
self
.
alternate_course_run_key
),
]),
]
),
]
else
:
# SAME_COURSE
return
[
ProgramFactory
(
courses
=
[
CourseFactory
(
course_runs
=
[
CourseRunFactory
(
key
=
self
.
course_run_key
),
CourseRunFactory
(
key
=
self
.
alternate_course_run_key
),
]),
]
),
]
@ddt.data
(
True
,
False
)
def
test_handle
(
self
,
commit
,
mock_task
,
mock_get_programs
):
"""
...
...
@@ -112,49 +164,10 @@ class BackpopulateProgramCredentialsTests(CatalogIntegrationMixin, CredentialsAp
# The task should be called for both users since professional and no-id-professional are equivalent.
mock_task
.
assert_has_calls
([
mock
.
call
(
self
.
alice
.
username
),
mock
.
call
(
self
.
bob
.
username
)])
@ddt.data
(
[
ProgramFactory
(
courses
=
[
CourseFactory
(
course_runs
=
[
CourseRunFactory
(
key
=
course_run_key
),
]),
]
),
ProgramFactory
(
courses
=
[
CourseFactory
(
course_runs
=
[
CourseRunFactory
(
key
=
alternate_course_run_key
),
]),
]
),
],
[
ProgramFactory
(
courses
=
[
CourseFactory
(
course_runs
=
[
CourseRunFactory
(
key
=
course_run_key
),
]),
CourseFactory
(
course_runs
=
[
CourseRunFactory
(
key
=
alternate_course_run_key
),
]),
]
),
],
[
ProgramFactory
(
courses
=
[
CourseFactory
(
course_runs
=
[
CourseRunFactory
(
key
=
course_run_key
),
CourseRunFactory
(
key
=
alternate_course_run_key
),
]),
]
),
],
)
def
test_handle_flatten
(
self
,
data
,
mock_task
,
mock_get_programs
):
@ddt.data
(
SEPARATE_PROGRAMS
,
SEPARATE_COURSES
,
SAME_COURSE
)
def
test_handle_flatten
(
self
,
hierarchy_type
,
mock_task
,
mock_get_programs
):
"""Verify that program structures are flattened correctly."""
mock_get_programs
.
return_value
=
data
mock_get_programs
.
return_value
=
self
.
_get_programs_data
(
hierarchy_type
)
GeneratedCertificateFactory
(
user
=
self
.
alice
,
...
...
openedx/core/djangoapps/user_api/validation/tests/test_views.py
View file @
fd6af6b0
...
...
@@ -53,11 +53,11 @@ class RegistrationValidationViewTests(test_utils.ApiTestCase):
)
@ddt.data
(
[
'name'
,
(
name
for
name
in
testutils
.
VALID_NAMES
)
],
[
'email'
,
(
email
for
email
in
testutils
.
VALID_EMAILS
)
],
[
'password'
,
(
password
for
password
in
testutils
.
VALID_PASSWORDS
)
],
[
'username'
,
(
username
for
username
in
testutils
.
VALID_USERNAMES
)
],
[
'country'
,
(
country
for
country
in
testutils
.
VALID_COUNTRIES
)
]
[
'name'
,
[
name
for
name
in
testutils
.
VALID_NAMES
]
],
[
'email'
,
[
email
for
email
in
testutils
.
VALID_EMAILS
]
],
[
'password'
,
[
password
for
password
in
testutils
.
VALID_PASSWORDS
]
],
[
'username'
,
[
username
for
username
in
testutils
.
VALID_USERNAMES
]
],
[
'country'
,
[
country
for
country
in
testutils
.
VALID_COUNTRIES
]
]
)
@ddt.unpack
def
test_positive_validation_decision
(
self
,
form_field_name
,
user_data
):
...
...
@@ -71,11 +71,11 @@ class RegistrationValidationViewTests(test_utils.ApiTestCase):
@ddt.data
(
# Skip None type for invalidity checks.
[
'name'
,
(
name
for
name
in
testutils
.
INVALID_NAMES
[
1
:])
],
[
'email'
,
(
email
for
email
in
testutils
.
INVALID_EMAILS
[
1
:])
],
[
'password'
,
(
password
for
password
in
testutils
.
INVALID_PASSWORDS
[
1
:])
],
[
'username'
,
(
username
for
username
in
testutils
.
INVALID_USERNAMES
[
1
:])
],
[
'country'
,
(
country
for
country
in
testutils
.
INVALID_COUNTRIES
[
1
:])
]
[
'name'
,
[
name
for
name
in
testutils
.
INVALID_NAMES
[
1
:]]
],
[
'email'
,
[
email
for
email
in
testutils
.
INVALID_EMAILS
[
1
:]]
],
[
'password'
,
[
password
for
password
in
testutils
.
INVALID_PASSWORDS
[
1
:]]
],
[
'username'
,
[
username
for
username
in
testutils
.
INVALID_USERNAMES
[
1
:]]
],
[
'country'
,
[
country
for
country
in
testutils
.
INVALID_COUNTRIES
[
1
:]]
]
)
@ddt.unpack
def
test_negative_validation_decision
(
self
,
form_field_name
,
user_data
):
...
...
openedx/core/djangoapps/util/tests/test_user_messages.py
View file @
fd6af6b0
...
...
@@ -6,6 +6,8 @@ import ddt
from
django.contrib.messages.middleware
import
MessageMiddleware
from
django.test
import
RequestFactory
,
TestCase
from
common.test.utils
import
normalize_repr
from
openedx.core.djangolib.markup
import
HTML
,
Text
from
student.tests.factories
import
UserFactory
...
...
@@ -60,10 +62,10 @@ class UserMessagesTestCase(TestCase):
self
.
assertEquals
(
messages
[
0
]
.
icon_class
,
expected_icon_class
)
@ddt.data
(
(
PageLevelMessages
.
register_error_message
,
UserMessageType
.
ERROR
),
(
PageLevelMessages
.
register_info_message
,
UserMessageType
.
INFO
),
(
PageLevelMessages
.
register_success_message
,
UserMessageType
.
SUCCESS
),
(
PageLevelMessages
.
register_warning_message
,
UserMessageType
.
WARNING
),
(
normalize_repr
(
PageLevelMessages
.
register_error_message
)
,
UserMessageType
.
ERROR
),
(
normalize_repr
(
PageLevelMessages
.
register_info_message
)
,
UserMessageType
.
INFO
),
(
normalize_repr
(
PageLevelMessages
.
register_success_message
)
,
UserMessageType
.
SUCCESS
),
(
normalize_repr
(
PageLevelMessages
.
register_warning_message
)
,
UserMessageType
.
WARNING
),
)
@ddt.unpack
def
test_message_type
(
self
,
register_message_function
,
expected_message_type
):
...
...
openedx/core/lib/xblock_builtin/xblock_discussion/tests.py
View file @
fd6af6b0
...
...
@@ -16,7 +16,16 @@ from xblock.fields import ScopeIds, UNIQUE_ID, NO_CACHE_VALUE
from
xblock.runtime
import
Runtime
def
attribute_pair_repr
(
self
):
"""
Custom string representation for the AttributePair namedtuple which is
consistent between test runs.
"""
return
'<AttributePair name={}>'
.
format
(
self
.
name
)
AttributePair
=
namedtuple
(
"AttributePair"
,
[
"name"
,
"value"
])
AttributePair
.
__repr__
=
attribute_pair_repr
ID_ATTR_NAMES
=
(
"discussion_id"
,
"id"
,)
...
...
openedx/features/course_experience/tests/views/test_course_outline.py
View file @
fd6af6b0
...
...
@@ -20,8 +20,6 @@ from xmodule.course_module import DEFAULT_START_DATE
from
.test_course_home
import
course_home_url
TEST_PASSWORD
=
'test'
FUTURE_DAY
=
datetime
.
datetime
.
now
()
+
datetime
.
timedelta
(
days
=
30
)
PAST_DAY
=
datetime
.
datetime
.
now
()
-
datetime
.
timedelta
(
days
=
30
)
class
TestCourseOutlinePage
(
SharedModuleStoreTestCase
):
...
...
@@ -343,14 +341,22 @@ class TestEmptyCourseOutlinePage(SharedModuleStoreTestCase):
"""
Test the new course outline view.
"""
FUTURE_DAY
=
'future_day'
PAST_DAY
=
'past_day'
DATES
=
{
'default_start_date'
:
DEFAULT_START_DATE
,
FUTURE_DAY
:
datetime
.
datetime
.
now
()
+
datetime
.
timedelta
(
days
=
30
),
PAST_DAY
:
datetime
.
datetime
.
now
()
-
datetime
.
timedelta
(
days
=
30
),
}
@ddt.data
(
(
FUTURE_DAY
,
'This course has not started yet, and will launch on'
),
(
PAST_DAY
,
"We're still working on course content."
),
(
DEFAULT_START_DATE
,
'This course has not started yet.'
),
(
'default_start_date'
,
'This course has not started yet.'
),
)
@ddt.unpack
def
test_empty_course_rendering
(
self
,
start_date
,
expected_text
):
course
=
CourseFactory
.
create
(
start
=
s
tart_date
)
def
test_empty_course_rendering
(
self
,
start_date
_name
,
expected_text
):
course
=
CourseFactory
.
create
(
start
=
s
elf
.
DATES
[
start_date_name
]
)
test_user
=
UserFactory
(
password
=
TEST_PASSWORD
)
CourseEnrollment
.
enroll
(
test_user
,
course
.
id
)
self
.
client
.
login
(
username
=
test_user
.
username
,
password
=
TEST_PASSWORD
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment