@@ -561,3 +561,131 @@ def test_forgejo_post_comment_calls_post(self, mock_request):
561561 json = {"body" : "Test comment" },
562562 timeout = self .forgejo .default_timeout ,
563563 )
564+
565+
566+ class SourceHutIntegrationTestCase (TestCase ):
567+ def setUp (self ):
568+ patcher = mock .patch ("workflow.models.Request.handle_integrations" , return_value = None )
569+ self .mock_handle_integrations = patcher .start ()
570+ self .addCleanup (patcher .stop )
571+
572+ self .dataspace = Dataspace .objects .create (name = "nexB" )
573+ self .dataspace .set_configuration ("sourcehut_token" , "fake-token" )
574+ self .super_user = create_superuser ("nexb_user" , self .dataspace )
575+ self .component_ct = ContentType .objects .get (
576+ app_label = "component_catalog" , model = "component"
577+ )
578+ self .request_template = RequestTemplate .objects .create (
579+ name = "SourceHut Template" ,
580+ description = "Integration test template" ,
581+ content_type = self .component_ct ,
582+ dataspace = self .dataspace ,
583+ issue_tracker_id = "https://todo.sr.ht/~username/project-name" ,
584+ )
585+ self .question = Question .objects .create (
586+ template = self .request_template ,
587+ label = "Example Question" ,
588+ input_type = "TextField" ,
589+ position = 0 ,
590+ dataspace = self .dataspace ,
591+ )
592+ self .request = self .request_template .create_request (
593+ title = "Example Request" ,
594+ requester = self .super_user ,
595+ serialized_data = '{"Example Question": "Some value"}' ,
596+ )
597+ self .sourcehut = SourceHutIntegration (dataspace = self .dataspace )
598+
599+ def test_sourcehut_extract_sourcehut_project_valid_url (self ):
600+ url = "https://todo.sr.ht/~user/project"
601+ result = SourceHutIntegration .extract_sourcehut_project (url )
602+ self .assertEqual (result , "~user/project" )
603+
604+ def test_sourcehut_extract_sourcehut_project_invalid_host (self ):
605+ with self .assertRaises (ValueError ):
606+ SourceHutIntegration .extract_sourcehut_project ("https://example.com/~user/project" )
607+
608+ def test_sourcehut_extract_sourcehut_project_invalid_path_format (self ):
609+ with self .assertRaises (ValueError ):
610+ SourceHutIntegration .extract_sourcehut_project ("https://todo.sr.ht/user/project" )
611+
612+ def test_sourcehut_get_headers_returns_auth_header (self ):
613+ headers = self .sourcehut .get_headers ()
614+ self .assertEqual (
615+ headers ,
616+ {
617+ "Authorization" : "Bearer fake-token" ,
618+ "Content-Type" : "application/json" ,
619+ },
620+ )
621+
622+ def test_sourcehut_make_issue_title (self ):
623+ title = self .sourcehut .make_issue_title (self .request )
624+ self .assertEqual (title , "[DEJACODE] Example Request" )
625+
626+ def test_sourcehut_make_issue_body_contains_question (self ):
627+ body = self .sourcehut .make_issue_body (self .request )
628+ self .assertIn ("### Example Question" , body )
629+ self .assertIn ("Some value" , body )
630+
631+ @mock .patch .object (SourceHutIntegration , "post" )
632+ @mock .patch .object (SourceHutIntegration , "get_tracker_id" , return_value = 1 )
633+ def test_sourcehut_create_issue_calls_post (self , mock_get_tracker_id , mock_post ):
634+ mock_post .return_value = {"data" : {"submitTicket" : {"id" : 123 , "subject" : "Issue Title" }}}
635+
636+ issue = self .sourcehut .create_issue (
637+ repo_id = "~user/project" ,
638+ title = "Issue Title" ,
639+ body = "Issue Body" ,
640+ )
641+
642+ self .assertEqual (issue ["id" ], 123 )
643+ mock_post .assert_called_once ()
644+ mock_get_tracker_id .assert_called_once_with ("~user/project" )
645+
646+ @mock .patch .object (SourceHutIntegration , "post" )
647+ @mock .patch .object (SourceHutIntegration , "get_tracker_id" , return_value = 1 )
648+ def test_sourcehut_update_issue_calls_post (self , mock_get_tracker_id , mock_post ):
649+ mock_post .return_value = {
650+ "data" : {
651+ "updateTicket" : {
652+ "id" : 123 ,
653+ "subject" : "Updated title" ,
654+ "status" : "REPORTED" ,
655+ }
656+ }
657+ }
658+
659+ response = self .sourcehut .update_issue (
660+ repo_id = "~user/project" ,
661+ issue_id = 123 ,
662+ title = "Updated title" ,
663+ body = "Updated body" ,
664+ )
665+
666+ self .assertEqual (response ["id" ], 123 )
667+ mock_post .assert_called_once ()
668+ mock_get_tracker_id .assert_called_once_with ("~user/project" )
669+
670+ @mock .patch .object (SourceHutIntegration , "post" )
671+ @mock .patch .object (SourceHutIntegration , "get_tracker_id" , return_value = 1 )
672+ def test_sourcehut_post_comment_calls_post (self , mock_get_tracker_id , mock_post ):
673+ mock_post .return_value = {
674+ "data" : {
675+ "submitComment" : {
676+ "id" : 77 ,
677+ "created" : "2025-08-12T12:00:00Z" ,
678+ "ticket" : {"id" : 123 , "subject" : "Example Request" },
679+ }
680+ }
681+ }
682+
683+ response = self .sourcehut .post_comment (
684+ repo_id = "~user/project" ,
685+ issue_id = 123 ,
686+ comment_body = "Test comment" ,
687+ )
688+
689+ self .assertEqual (response ["id" ], 77 )
690+ mock_post .assert_called_once ()
691+ mock_get_tracker_id .assert_called_once_with ("~user/project" )
0 commit comments