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 ...@@ -134,14 +134,20 @@ class CatalogViewSetTests(ElasticsearchTestMixin, SerializationMixin, OAuth2Mixi
def test_courses(self): def test_courses(self):
""" Verify the endpoint returns the list of courses contained in the catalog. """ """ Verify the endpoint returns the list of courses contained in the catalog. """
url = reverse('api:v1:catalog-courses', kwargs={'id': self.catalog.id}) url = reverse('api:v1:catalog-courses', kwargs={'id': self.catalog.id})
SeatFactory(course_run=self.course_run)
courses = [self.course] courses = [self.course]
# These courses/course runs should not be returned because they are no longer open for enrollment. # 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) enrollment_end = datetime.datetime.now(pytz.UTC) - datetime.timedelta(days=30)
CourseRunFactory(enrollment_end=enrollment_end, course__title='ABC Test Course 2') excluded_runs = [
CourseRunFactory(enrollment_end=enrollment_end, course=self.course) CourseRunFactory(enrollment_end=enrollment_end, course__title='ABC Test Course 2'),
CourseRunFactory(enrollment_end=enrollment_end, course=self.course),
with self.assertNumQueries(42): ]
for course_run in excluded_runs:
SeatFactory(course_run=course_run)
with self.assertNumQueries(45):
response = self.client.get(url) response = self.client.get(url)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertListEqual(response.data['results'], self.serialize_catalog_course(courses, many=True)) 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 ...@@ -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.core.tests.mixins import ElasticsearchTestMixin
from course_discovery.apps.course_metadata.choices import ProgramStatus from course_discovery.apps.course_metadata.choices import ProgramStatus
from course_discovery.apps.course_metadata.models import CourseRun 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 @ddt.ddt
...@@ -168,8 +170,12 @@ class CourseRunViewSetTests(SerializationMixin, ElasticsearchTestMixin, APITestC ...@@ -168,8 +170,12 @@ class CourseRunViewSetTests(SerializationMixin, ElasticsearchTestMixin, APITestC
""" Verify the endpoint filters course runs to those that are marketable. """ """ Verify the endpoint filters course runs to those that are marketable. """
CourseRun.objects.all().delete() CourseRun.objects.all().delete()
expected = CourseRunFactory.create_batch(3, course__partner=self.partner) 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=None, course__partner=self.partner)
CourseRunFactory.create_batch(3, slug='', course__partner=self.partner) CourseRunFactory.create_batch(3, slug='', course__partner=self.partner)
url = reverse('api:v1:course_run-list') + '?marketable=1' url = reverse('api:v1:course_run-list') + '?marketable=1'
self.assert_list_results(url, expected) self.assert_list_results(url, expected)
......
...@@ -46,13 +46,22 @@ class CourseRunQuerySet(models.QuerySet): ...@@ -46,13 +46,22 @@ class CourseRunQuerySet(models.QuerySet):
def marketable(self): def marketable(self):
""" Returns CourseRuns that can be marketed to learners. """ 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: Returns:
QuerySet 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): class ProgramQuerySet(models.QuerySet):
......
...@@ -7,7 +7,7 @@ from django.test import TestCase ...@@ -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 CourseRunStatus
from course_discovery.apps.course_metadata.choices import ProgramStatus 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.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): class CourseQuerySetTests(TestCase):
...@@ -71,24 +71,39 @@ class CourseRunQuerySetTests(TestCase): ...@@ -71,24 +71,39 @@ class CourseRunQuerySetTests(TestCase):
def test_marketable(self): def test_marketable(self):
""" Verify the method filters CourseRuns to those with slugs. """ """ Verify the method filters CourseRuns to those with slugs. """
course_run = CourseRunFactory() course_run = CourseRunFactory()
SeatFactory(course_run=course_run)
self.assertEqual(list(CourseRun.objects.marketable()), [course_run]) self.assertEqual(list(CourseRun.objects.marketable()), [course_run])
@ddt.data(None, '') @ddt.data(None, '')
def test_marketable_exclusions(self, slug): def test_marketable_exclusions(self, slug):
""" Verify the method excludes CourseRuns without a slug. """ """ Verify the method excludes CourseRuns without a slug. """
CourseRunFactory(slug=slug) course_run = CourseRunFactory(slug=slug)
self.assertEqual(CourseRun.objects.marketable().count(), 0) SeatFactory(course_run=course_run)
@ddt.data( self.assertEqual(CourseRun.objects.marketable().exists(), False)
(CourseRunStatus.Unpublished, 0),
(CourseRunStatus.Published, 1) @ddt.data(True, False)
) def test_marketable_seats_exclusions(self, has_seats):
@ddt.unpack """ Verify that the method excludes CourseRuns without seats. """
def test_marketable_unpublished_exclusions(self, status, count): 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. """ """ 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 @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