Commit b578367c by Renzo Lucioni

Extend CourseRunQuerySet marketable filter to exclude runs without seats

Runs without seats should not be considered marketable.

ECOM-6768
parent b3957384
......@@ -134,14 +134,20 @@ class CatalogViewSetTests(ElasticsearchTestMixin, SerializationMixin, OAuth2Mixi
def test_courses(self):
""" Verify the endpoint returns the list of courses contained in the catalog. """
url = reverse('api:v1:catalog-courses', kwargs={'id': self.catalog.id})
SeatFactory(course_run=self.course_run)
courses = [self.course]
# These courses/course runs should not be returned because they are no longer open for enrollment.
enrollment_end = datetime.datetime.now(pytz.UTC) - datetime.timedelta(days=30)
CourseRunFactory(enrollment_end=enrollment_end, course__title='ABC Test Course 2')
CourseRunFactory(enrollment_end=enrollment_end, course=self.course)
with self.assertNumQueries(42):
excluded_runs = [
CourseRunFactory(enrollment_end=enrollment_end, course__title='ABC Test Course 2'),
CourseRunFactory(enrollment_end=enrollment_end, course=self.course),
]
for course_run in excluded_runs:
SeatFactory(course_run=course_run)
with self.assertNumQueries(45):
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertListEqual(response.data['results'], self.serialize_catalog_course(courses, many=True))
......
......@@ -13,7 +13,9 @@ from course_discovery.apps.core.tests.factories import UserFactory
from course_discovery.apps.core.tests.mixins import ElasticsearchTestMixin
from course_discovery.apps.course_metadata.choices import ProgramStatus
from course_discovery.apps.course_metadata.models import CourseRun
from course_discovery.apps.course_metadata.tests.factories import CourseRunFactory, PartnerFactory, ProgramFactory
from course_discovery.apps.course_metadata.tests.factories import (
CourseRunFactory, PartnerFactory, ProgramFactory, SeatFactory
)
@ddt.ddt
......@@ -168,8 +170,12 @@ class CourseRunViewSetTests(SerializationMixin, ElasticsearchTestMixin, APITestC
""" Verify the endpoint filters course runs to those that are marketable. """
CourseRun.objects.all().delete()
expected = CourseRunFactory.create_batch(3, course__partner=self.partner)
for course_run in expected:
SeatFactory(course_run=course_run)
CourseRunFactory.create_batch(3, slug=None, course__partner=self.partner)
CourseRunFactory.create_batch(3, slug='', course__partner=self.partner)
url = reverse('api:v1:course_run-list') + '?marketable=1'
self.assert_list_results(url, expected)
......
......@@ -46,13 +46,22 @@ class CourseRunQuerySet(models.QuerySet):
def marketable(self):
""" Returns CourseRuns that can be marketed to learners.
A CourseRun is considered marketable if it has a defined slug and has been published.
A CourseRun is considered marketable if it has a defined slug, has seats, and has been published.
Returns:
QuerySet
"""
return self.exclude(slug__isnull=True).exclude(slug='').filter(status=CourseRunStatus.Published)
return self.exclude(
slug__isnull=True
).exclude(
slug=''
).exclude(
# This will exclude any course run without seats (e.g., CCX runs).
seats__isnull=True
).filter(
status=CourseRunStatus.Published
)
class ProgramQuerySet(models.QuerySet):
......
......@@ -7,7 +7,7 @@ from django.test import TestCase
from course_discovery.apps.course_metadata.choices import CourseRunStatus
from course_discovery.apps.course_metadata.choices import ProgramStatus
from course_discovery.apps.course_metadata.models import Course, CourseRun, Program
from course_discovery.apps.course_metadata.tests.factories import CourseRunFactory, ProgramFactory
from course_discovery.apps.course_metadata.tests.factories import CourseRunFactory, ProgramFactory, SeatFactory
class CourseQuerySetTests(TestCase):
......@@ -71,24 +71,39 @@ class CourseRunQuerySetTests(TestCase):
def test_marketable(self):
""" Verify the method filters CourseRuns to those with slugs. """
course_run = CourseRunFactory()
SeatFactory(course_run=course_run)
self.assertEqual(list(CourseRun.objects.marketable()), [course_run])
@ddt.data(None, '')
def test_marketable_exclusions(self, slug):
""" Verify the method excludes CourseRuns without a slug. """
CourseRunFactory(slug=slug)
self.assertEqual(CourseRun.objects.marketable().count(), 0)
course_run = CourseRunFactory(slug=slug)
SeatFactory(course_run=course_run)
@ddt.data(
(CourseRunStatus.Unpublished, 0),
(CourseRunStatus.Published, 1)
)
@ddt.unpack
def test_marketable_unpublished_exclusions(self, status, count):
self.assertEqual(CourseRun.objects.marketable().exists(), False)
@ddt.data(True, False)
def test_marketable_seats_exclusions(self, has_seats):
""" Verify that the method excludes CourseRuns without seats. """
course_run = CourseRunFactory()
if has_seats:
SeatFactory(course_run=course_run)
self.assertEqual(CourseRun.objects.marketable().exists(), has_seats)
@ddt.data(True, False)
def test_marketable_unpublished_exclusions(self, is_published):
""" Verify the method excludes CourseRuns with Unpublished status. """
CourseRunFactory(status=status)
course_run = CourseRunFactory(status=CourseRunStatus.Unpublished)
SeatFactory(course_run=course_run)
if is_published:
course_run.status = CourseRunStatus.Published
course_run.save()
self.assertEqual(CourseRun.objects.marketable().count(), count)
self.assertEqual(CourseRun.objects.marketable().exists(), is_published)
@ddt.ddt
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment