|
1 | 1 | """Tests for Canvas ETL functionality""" |
2 | 2 |
|
3 | 3 | import zipfile |
| 4 | +from pathlib import Path |
4 | 5 |
|
5 | 6 | import pytest |
6 | 7 |
|
|
9 | 10 | parse_canvas_settings, |
10 | 11 | parse_module_meta, |
11 | 12 | run_for_canvas_archive, |
| 13 | + transform_canvas_content_files, |
12 | 14 | ) |
13 | 15 | from learning_resources.etl.constants import ETLSource |
| 16 | +from learning_resources.etl.utils import get_edx_module_id |
14 | 17 | from learning_resources.factories import ( |
| 18 | + ContentFileFactory, |
15 | 19 | LearningResourceFactory, |
16 | 20 | LearningResourcePlatformFactory, |
| 21 | + LearningResourceRunFactory, |
17 | 22 | ) |
18 | 23 | from learning_resources.models import LearningResource |
| 24 | +from learning_resources_search.constants import CONTENT_FILE_TYPE |
19 | 25 |
|
20 | 26 | pytestmark = pytest.mark.django_db |
21 | 27 |
|
@@ -250,3 +256,85 @@ def test_parse_module_meta_handles_missing_identifierref(tmp_path): |
250 | 256 | assert "active" in result |
251 | 257 | assert len(result["active"]) == 0 |
252 | 258 | assert len(result["unpublished"]) == 0 |
| 259 | + |
| 260 | + |
| 261 | +def test_transform_canvas_content_files_removes_unpublished_content(mocker, tmp_path): |
| 262 | + """ |
| 263 | + Test that transform_canvas_content_files removes content files not marked as published. |
| 264 | + """ |
| 265 | + |
| 266 | + # Setup: create a fake run with some content files |
| 267 | + resource = LearningResourceFactory.create(etl_source=ETLSource.canvas.name) |
| 268 | + run = LearningResourceRunFactory.create(learning_resource=resource) |
| 269 | + |
| 270 | + published_path = "/test/published/file1.html" |
| 271 | + unpublished_path = "/test/unpublished/file2.html" |
| 272 | + unpublished_cf = ContentFileFactory.create( |
| 273 | + run=run, published=True, key=get_edx_module_id(unpublished_path, run) |
| 274 | + ) |
| 275 | + module_xml = b"""<?xml version="1.0" encoding="UTF-8"?> |
| 276 | + <modules xmlns="http://canvas.instructure.com/xsd/cccv1p0"> |
| 277 | + <module> |
| 278 | + <title>Module 1</title> |
| 279 | + <items> |
| 280 | + <item> |
| 281 | + <workflow_state>active</workflow_state> |
| 282 | + <title>Item 1</title> |
| 283 | + <identifierref>RES1</identifierref> |
| 284 | + <content_type>resource</content_type> |
| 285 | + </item> |
| 286 | + <item> |
| 287 | + <workflow_state>unpublished</workflow_state> |
| 288 | + <title>Item 2</title> |
| 289 | + <identifierref>RES2</identifierref> |
| 290 | + <content_type>resource</content_type> |
| 291 | + </item> |
| 292 | + </items> |
| 293 | + </module> |
| 294 | + </modules> |
| 295 | + """ |
| 296 | + manifest_xml = bytes( |
| 297 | + f"""<?xml version="1.0" encoding="UTF-8"?> |
| 298 | + <manifest xmlns="http://www.imsglobal.org/xsd/imsccv1p1/imscp_v1p1"> |
| 299 | + <resources> |
| 300 | + <resource identifier="RES1" type="webcontent"> |
| 301 | + <file href="{published_path}"/> |
| 302 | + </resource> |
| 303 | + <resource identifier="RES2" type="webcontent"> |
| 304 | + <file href="{unpublished_path}"/> |
| 305 | + </resource> |
| 306 | + </resources> |
| 307 | + <organizations> |
| 308 | + <organization> |
| 309 | + <item identifierref="RES1"> |
| 310 | + <title>Item 1</title> |
| 311 | + </item> |
| 312 | + <item identifierref="RES2"> |
| 313 | + <title>Item 2</title> |
| 314 | + </item> |
| 315 | + </organization> |
| 316 | + </organizations> |
| 317 | + </manifest> |
| 318 | + """, |
| 319 | + "utf-8", |
| 320 | + ) |
| 321 | + zip_path = tmp_path / "canvas_course.zip" |
| 322 | + with zipfile.ZipFile(zip_path, "w") as zf: |
| 323 | + zf.writestr("course_settings/module_meta.xml", module_xml) |
| 324 | + zf.writestr("imsmanifest.xml", manifest_xml) |
| 325 | + zf.writestr(published_path, "content") |
| 326 | + zf.writestr(unpublished_path, "content") |
| 327 | + mocker.patch( |
| 328 | + "learning_resources.etl.utils.extract_text_metadata", |
| 329 | + return_value={"content": "test"}, |
| 330 | + ) |
| 331 | + bulk_unpub = mocker.patch( |
| 332 | + "learning_resources.etl.canvas.bulk_resources_unpublished_actions" |
| 333 | + ) |
| 334 | + |
| 335 | + # Create a fake zipfile with the published file |
| 336 | + |
| 337 | + list(transform_canvas_content_files(Path(zip_path), run, overwrite=True)) |
| 338 | + |
| 339 | + # Ensure unpublished content is deleted and unpublished actions called |
| 340 | + bulk_unpub.assert_called_once_with([unpublished_cf.id], CONTENT_FILE_TYPE) |
0 commit comments