@@ -728,6 +728,86 @@ def test_multiple_question_types(self):
728728 # we are deliberately changing the archive generation algorithm for perseus files.
729729 self .assertEqual (exercise_file .checksum , "94de065d485e52d56c3032074044e7c3" )
730730
731+ def test_image_key_full_path_regression (self ):
732+ """Regression test for image key containing full path in Perseus files.
733+
734+ This test ensures that the 'images' object in Perseus JSON files uses the full path
735+ as the key (${IMG_PLACEHOLDER}/images/filename.ext) rather than just the filename.
736+
737+ Bug: The image key in the 'images' object was being set to just the filename
738+ instead of the full path with IMG_PLACEHOLDER prefix.
739+ """
740+ # Create an image file
741+ image_file = fileobj_exercise_image ()
742+
743+ # Create a question with image that has dimensions (to trigger images object generation)
744+ image_url = exercises .CONTENT_STORAGE_FORMAT .format (image_file .filename ())
745+ question_text = f"Identify the shape: "
746+ item = self ._create_assessment_item (
747+ exercises .SINGLE_SELECTION ,
748+ question_text ,
749+ [
750+ {"answer" : "Circle" , "correct" : True , "order" : 1 },
751+ {"answer" : "Square" , "correct" : False , "order" : 2 },
752+ ],
753+ )
754+
755+ # Associate the image with the assessment item
756+ image_file .assessment_item = item
757+ image_file .save ()
758+
759+ # Create the exercise data
760+ exercise_data = {
761+ "mastery_model" : exercises .M_OF_N ,
762+ "randomize" : True ,
763+ "n" : 1 ,
764+ "m" : 1 ,
765+ "all_assessment_items" : [item .assessment_id ],
766+ "assessment_mapping" : {item .assessment_id : exercises .SINGLE_SELECTION },
767+ }
768+
769+ # Create the Perseus exercise
770+ self ._create_perseus_zip (exercise_data )
771+
772+ # Verify that a file was created
773+ exercise_file = self .exercise_node .files .get (preset_id = format_presets .EXERCISE )
774+
775+ # Validate the zip file
776+ zip_file , _ = self ._validate_perseus_zip (exercise_file )
777+
778+ # Get the Perseus item JSON content
779+ item_json = json .loads (
780+ zip_file .read (f"{ item .assessment_id } .json" ).decode ("utf-8" )
781+ )
782+
783+ # The critical regression check: images object keys should contain full path
784+ question_images = item_json ["question" ]["images" ]
785+
786+ # Should have exactly one image entry
787+ self .assertEqual (
788+ len (question_images ),
789+ 1 ,
790+ f"Expected 1 image in images object, got { len (question_images )} : { list (question_images .keys ())} " ,
791+ )
792+
793+ # Get the image key from the images object
794+ image_key = list (question_images .keys ())[0 ]
795+
796+ # The key should be the full path, not just the filename
797+ expected_full_path = (
798+ f"${ exercises .IMG_PLACEHOLDER } /images/{ image_file .filename ()} "
799+ )
800+ self .assertEqual (
801+ image_key ,
802+ expected_full_path ,
803+ f"Image key should be '{ expected_full_path } ' but got: '{ image_key } '" ,
804+ )
805+
806+ # Verify the image has the expected dimensions
807+ image_data = question_images [image_key ]
808+ self .assertEqual (image_data ["width" ], 100 )
809+ self .assertEqual (image_data ["height" ], 100 )
810+
731811 def _test_image_resizing_in_field (self , field_type ):
732812 """
733813 Helper method to test image resizing in different fields (question, answer, hint)
0 commit comments