Skip to content

Commit

Permalink
feat: change program slugs depending on a flag.
Browse files Browse the repository at this point in the history
Based on the waffle flag, slug pattern is selected based on the
roll-out and roll-back phases.
  • Loading branch information
uzairr committed Oct 4, 2023
1 parent 0dd3c57 commit 4fb52f9
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 38 deletions.
1 change: 1 addition & 0 deletions course_discovery/apps/course_metadata/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
r'boot-camps\/[a-zA-Z0-9-_]+\/[a-zA-Z0-9-_]+$'
)
SUBDIRECTORY_PROGRAM_SLUG_FORMAT_REGEX = r'[a-zA-Z0-9-_]+\/[a-zA-Z0-9-_\/]+$'
PROGRAM_SLUG_FORMAT_REGEX = r'[a-zA-Z0-9-_]+'

SLUG_FORMAT_REGEX = r'[a-zA-Z0-9-_]+$'

Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
import tempfile
import uuid

import ddt
import mock
from django.conf import settings
from django.core.files.uploadedfile import SimpleUploadedFile
from django.core.management import CommandError, call_command
from django.test import TestCase
from edx_toggles.toggles.testutils import override_waffle_switch
from testfixtures import LogCapture

from course_discovery.apps.course_metadata.tests.factories import MigrateProgramSlugConfigurationFactory, ProgramFactory
from course_discovery.apps.course_metadata.toggles import IS_SUBDIRECTORY_SLUG_FORMAT_ENABLED

LOGGER_PATH = 'course_discovery.apps.course_metadata.management.commands.update_program_url_slugs'


@ddt.ddt
class UpdateProgramUrlSlugCommandTests(TestCase):
"""
Test suite for update_program_url_slug management command.
Expand Down Expand Up @@ -85,9 +89,10 @@ def test_success_flow__through_configuration_model(self, mock_send_email_for_slu
current_slug_program2 = self.program2.marketing_slug
current_slug_program3 = self.program3.marketing_slug

call_command(
'update_program_url_slugs', args_from_database=True
)
with override_waffle_switch(IS_SUBDIRECTORY_SLUG_FORMAT_ENABLED, active=True):
call_command(
'update_program_url_slugs', args_from_database=True
)

log_capture.check_present(
(
Expand Down Expand Up @@ -147,10 +152,10 @@ def test_success_flow__through_csv_file_path(self, mock_send_email_for_slug_upda
current_slug_program1 = self.program1.marketing_slug
current_slug_program2 = self.program2.marketing_slug
current_slug_program3 = self.program3.marketing_slug

call_command(
'update_program_url_slugs', '--csv_file', csv_file.name
)
with override_waffle_switch(IS_SUBDIRECTORY_SLUG_FORMAT_ENABLED, active=True):
call_command(
'update_program_url_slugs', '--csv_file', csv_file.name
)

log_capture.check_present(
(
Expand Down Expand Up @@ -205,10 +210,10 @@ def test_invalid_program_uuid(self, mock_send_email_for_slug_updates):
)

_ = MigrateProgramSlugConfigurationFactory.create(csv_file=self.csv_file, enabled=True)

call_command(
'update_program_url_slugs', args_from_database=True
)
with override_waffle_switch(IS_SUBDIRECTORY_SLUG_FORMAT_ENABLED, active=True):
call_command(
'update_program_url_slugs', args_from_database=True
)

assert mock_send_email_for_slug_updates.call_count == 1
expected_msg = 'program_uuid,old_slug,new_slug,error\ninvalid-program-uuid,None,None,' \
Expand Down Expand Up @@ -237,31 +242,86 @@ def test_valid_program_uuid_not_existing_in_db(self, mock_send_email_for_slug_up
)

_ = MigrateProgramSlugConfigurationFactory.create(csv_file=self.csv_file, enabled=True)
with override_waffle_switch(IS_SUBDIRECTORY_SLUG_FORMAT_ENABLED, active=True):
with LogCapture(LOGGER_PATH) as log_capture:
call_command(
'update_program_url_slugs', args_from_database=True
)
expected_msg = f'Unable to locate Program instance with code {program_uuid}'

with LogCapture(LOGGER_PATH) as log_capture:
call_command(
'update_program_url_slugs', args_from_database=True
)
expected_msg = f'Unable to locate Program instance with code {program_uuid}'
log_capture.check_present(
(
LOGGER_PATH,
'INFO',
'Initiating Program URL slug updation flow.'
),
(
LOGGER_PATH,
'INFO',
expected_msg
),
)
assert mock_send_email_for_slug_updates.call_count == 1
expected_csv_msg = f'program_uuid,old_slug,new_slug,error\n{program_uuid},None,None,' \
f'Unable to locate Program instance with code {program_uuid}\n'

mock_send_email_for_slug_updates.assert_called_with(
expected_csv_msg,
settings.NOTIFY_SLUG_UPDATE_RECIPIENTS,
'Migrate Program Slugs Summary Report',
)

log_capture.check_present(
(
LOGGER_PATH,
'INFO',
'Initiating Program URL slug updation flow.'
),
(
LOGGER_PATH,
'INFO',
expected_msg
),
)
assert mock_send_email_for_slug_updates.call_count == 1
expected_csv_msg = f'program_uuid,old_slug,new_slug,error\n{program_uuid},None,None,' \
f'Unable to locate Program instance with code {program_uuid}\n'
@ddt.data(
('test-slug-program-1', False),
('category/sub-category/campus-name-course-name', True),
)
@ddt.unpack
def test_success_flow_with_flag_toggle(self, slug, state):
"""
Test that the command updates the marketing_slug for the programs according to two different formats
depending upon the subdirectory flag.
"""
self.csv_file_content = self.csv_header
self.csv_file_content += f'{self.program1.uuid},{slug}\n'

mock_send_email_for_slug_updates.assert_called_with(
expected_csv_msg,
settings.NOTIFY_SLUG_UPDATE_RECIPIENTS,
'Migrate Program Slugs Summary Report',
)
self.csv_file = SimpleUploadedFile(
name='test.csv',
content=self.csv_file_content.encode('utf-8'),
content_type='text/csv'
)
MigrateProgramSlugConfigurationFactory.create(csv_file=self.csv_file, enabled=True)

with mock.patch(LOGGER_PATH + '.send_email_for_slug_updates') as mock_send_email_for_slug_updates:
with LogCapture(LOGGER_PATH) as log_capture:
current_slug_program1 = self.program1.marketing_slug

with override_waffle_switch(IS_SUBDIRECTORY_SLUG_FORMAT_ENABLED, active=state):
call_command(
'update_program_url_slugs', args_from_database=True
)

log_capture.check_present(
(
LOGGER_PATH,
'INFO',
'Initiating Program URL slug updation flow.'
),
(
LOGGER_PATH,
'INFO',
f'Updated Program ({self.program1.uuid}) with slug: {current_slug_program1} '
f'to new url slug: {slug}'
),
)

assert mock_send_email_for_slug_updates.call_count == 1
self.program1.refresh_from_db()
assert self.program1.marketing_slug == slug
expected_msg = f'program_uuid,old_slug,new_slug,error\n{self.program1.uuid},{current_slug_program1},' \
f'{slug},None\n'

mock_send_email_for_slug_updates.assert_called_with(
expected_msg,
settings.NOTIFY_SLUG_UPDATE_RECIPIENTS,
'Migrate Program Slugs Summary Report',
)
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
from django.conf import settings
from django.core.management import BaseCommand, CommandError

from course_discovery.apps.course_metadata.constants import SUBDIRECTORY_PROGRAM_SLUG_FORMAT_REGEX
from course_discovery.apps.course_metadata.constants import (
PROGRAM_SLUG_FORMAT_REGEX, SUBDIRECTORY_PROGRAM_SLUG_FORMAT_REGEX
)
from course_discovery.apps.course_metadata.emails import send_email_for_slug_updates
from course_discovery.apps.course_metadata.models import MigrateProgramSlugConfiguration, Program
from course_discovery.apps.course_metadata.toggles import IS_SUBDIRECTORY_SLUG_FORMAT_ENABLED
from course_discovery.apps.course_metadata.utils import is_valid_uuid, transform_dict_keys

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -121,7 +124,10 @@ def validate_program_fields(self, program_uuid, new_url_slug):
True if uuid and slug is in valid format
"""
error = "Skipping uuid {} because of {}"
if not bool(re.match(SUBDIRECTORY_PROGRAM_SLUG_FORMAT_REGEX, new_url_slug)):
slug_pattern = SUBDIRECTORY_PROGRAM_SLUG_FORMAT_REGEX if IS_SUBDIRECTORY_SLUG_FORMAT_ENABLED.is_enabled() \
else PROGRAM_SLUG_FORMAT_REGEX

if not bool(re.fullmatch(slug_pattern, new_url_slug)):
self.update_slug_report(program_uuid, error.format(program_uuid, 'incorrect slug format'))
return False

Expand Down

0 comments on commit 4fb52f9

Please sign in to comment.