@@ -660,7 +660,11 @@ def test_subscribe_project_notifications(self):
660660 self .assertDictEqual (response .json (), {'isSubscriber' : True })
661661 self .assertTrue (self .collaborator_user .groups .filter (name = 'subscribers' ).exists ())
662662
663- def test_load_rna_seq_sample_data (self ):
663+ @mock .patch ('seqr.views.utils.permissions_utils.PM_USER_GROUP' , 'project-managers' )
664+ @mock .patch ('seqr.utils.file_utils.gzip.open' )
665+ @mock .patch ('seqr.utils.file_utils.os.path.isfile' )
666+ @mock .patch ('seqr.utils.file_utils.subprocess.Popen' )
667+ def test_load_rna_seq_sample_data (self , mock_subprocess , mock_does_file_exist , mock_open ):
664668 url = reverse (load_rna_seq_sample_data , args = ['RS000162_T_na19675_d2' ])
665669 self .check_pm_login (url )
666670
@@ -673,8 +677,10 @@ def test_load_rna_seq_sample_data(self):
673677 self .reset_logs ()
674678 parsed_file_lines = params ['parsed_file_data' ][sample_guid ].strip ().split ('\n ' )
675679
676- file_name = f'rna_sample_data__{ data_type } __2020-04-15T00:00:00'
677- not_found_logs = self ._set_file_not_found (file_name , sample_guid )
680+ file_name = f'{ self .TEMP_DIR } /rna_sample_data__{ data_type } __2020-04-15T00:00:00'
681+ not_found_logs = self ._set_file_not_found (
682+ f'{ file_name } /{ sample_guid } .json.gz' , mock_subprocess , mock_does_file_exist , mock_open ,
683+ )
678684
679685 body = {'fileName' : file_name , 'dataType' : data_type }
680686 response = self .client .post (url , content_type = 'application/json' , data = json .dumps (body ))
@@ -688,7 +694,8 @@ def test_load_rna_seq_sample_data(self):
688694 }),
689695 ])
690696
691- self ._add_file_iter ([row .encode ('utf-8' ) for row in parsed_file_lines ])
697+ file_lines = [row .encode ('utf-8' ) for row in parsed_file_lines ]
698+ self ._set_file_iter (file_lines , mock_subprocess , mock_does_file_exist , mock_open )
692699
693700 self .reset_logs ()
694701 response = self .client .post (url , content_type = 'application/json' , data = json .dumps (body ))
@@ -701,7 +708,7 @@ def test_load_rna_seq_sample_data(self):
701708 self .assertSetEqual ({model .sample .guid for model in models }, {sample_guid })
702709 self .assertTrue (all (model .sample .is_active for model in models ))
703710
704- subprocess_logs = self ._get_expected_read_file_subprocess_calls (file_name , sample_guid )
711+ subprocess_logs = self ._get_expected_read_file_subprocess_calls (mock_subprocess , file_name , sample_guid )
705712
706713 self .assert_json_logs (self .pm_user , [
707714 ('Loading outlier data for NA19675_1' , None ),
@@ -715,13 +722,30 @@ def test_load_rna_seq_sample_data(self):
715722 self .assertListEqual (list (params ['get_models_json' ](models )), params ['expected_models_json' ])
716723
717724 mismatch_row = {** json .loads (parsed_file_lines [0 ]), params .get ('mismatch_field' , 'p_value' ): '0.05' }
718- self ._add_file_iter ( [json .dumps (mismatch_row ).encode ('utf-8' )])
725+ self ._set_file_iter ( file_lines + [json .dumps (mismatch_row ).encode ('utf-8' )], mock_subprocess , mock_does_file_exist , mock_open )
719726 response = self .client .post (url , content_type = 'application/json' , data = json .dumps (body ))
720727 self .assertEqual (response .status_code , 400 )
721728 self .assertDictEqual (response .json (), {
722729 'error' : f'Error in { sample_guid .split ("_" , 1 )[- 1 ].upper ()} : mismatched entries for { params .get ("row_id" , mismatch_row ["gene_id" ])} '
723730 })
724731
732+ @staticmethod
733+ def _set_file_not_found (file_name , mock_subprocess , mock_does_file_exist , mock_open ):
734+ mock_does_file_exist .return_value = False
735+ mock_open .return_value .__enter__ .return_value .__iter__ .return_value = []
736+ return []
737+
738+ @staticmethod
739+ def _set_file_iter (stdout , mock_subprocess , mock_does_file_exist , mock_open ):
740+ mock_does_file_exist .return_value = True
741+ file_iter = mock_open .return_value .__enter__ .return_value .__iter__
742+ file_iter .return_value = stdout
743+
744+ @staticmethod
745+ def _get_expected_read_file_subprocess_calls (mock_subprocess , file_name , sample_guid ):
746+ mock_subprocess .assert_not_called ()
747+ return []
748+
725749
726750BASE_COLLABORATORS = [
727751 {'displayName' : 'Test Manager User' , 'email' : 'test_user_manager@test.com' , 'username' : 'test_user_manager' ,
@@ -745,6 +769,7 @@ class LocalProjectAPITest(AuthenticationTestCase, ProjectAPITest):
745769 PROJECT_COLLABORATOR_GROUPS = [{'name' : 'analysts' , 'hasViewPermissions' : True , 'hasEditPermissions' : True }]
746770 REQUIRED_FIELDS = ['name' , 'genomeVersion' ]
747771 HAS_EMPTY_PROJECT = True
772+ TEMP_DIR = '/tmp/rna_loading'
748773
749774 def _check_created_project_groups (self , project ):
750775 super ()._check_created_project_groups (project )
@@ -770,6 +795,7 @@ class AnvilProjectAPITest(AnvilAuthenticationTestCase, ProjectAPITest):
770795 PROJECT_COLLABORATORS = ANVIL_COLLABORATORS
771796 PROJECT_COLLABORATOR_GROUPS = None
772797 HAS_EMPTY_PROJECT = False
798+ TEMP_DIR = 'gs://seqr-scratch-temp'
773799
774800 def test_create_and_delete_project (self , * args , ** kwargs ):
775801 super (AnvilProjectAPITest , self ).test_create_and_delete_project (* args , ** kwargs )
@@ -857,3 +883,31 @@ def _assert_expected_project_families(self, *args, **kwargs):
857883 '@type' : 'type.googleapis.com/google.devtools.clouderrorreporting.v1beta1.ReportedErrorEvent' ,
858884 }),
859885 ])
886+
887+ @staticmethod
888+ def _set_file_not_found (file_name , mock_subprocess , mock_does_file_exist , mock_open ):
889+ mock_does_file_exist = mock .MagicMock ()
890+ mock_does_file_exist .stdout = [b'CommandException: One or more URLs matched no objects' ]
891+ mock_does_file_exist .wait .return_value = 1
892+ mock_subprocess .side_effect = [mock_does_file_exist ]
893+ return [
894+ (f'==> gsutil ls gs://seqr-scratch-temp/{ file_name } ' , None ),
895+ ('CommandException: One or more URLs matched no objects' , {'severity' : 'WARNING' }),
896+ ]
897+
898+ @staticmethod
899+ def _set_file_iter (stdout , mock_subprocess , mock_does_file_exist , mock_open ):
900+ mock_does_file_exist = mock .MagicMock ()
901+ mock_does_file_exist .wait .return_value = 0
902+ mock_file_iter = mock .MagicMock ()
903+ mock_file_iter .stdout = stdout
904+ mock_subprocess .side_effect = [mock_does_file_exist , mock_file_iter ]
905+
906+ @staticmethod
907+ def _get_expected_read_file_subprocess_calls (mock_subprocess , file_name , sample_guid ):
908+ gsutil_cat = f'gsutil cat gs://seqr-scratch-temp/{ file_name } /{ sample_guid } .json.gz | gunzip -c -q - '
909+ mock_subprocess .assert_called_with (gsutil_cat , stdout = - 1 , stderr = - 2 , shell = True ) # nosec
910+ return [
911+ (f'==> gsutil ls gs://seqr-scratch-temp/{ file_name } /{ sample_guid } .json.gz' , None ),
912+ (f'==> { gsutil_cat } ' , None ),
913+ ]
0 commit comments