@@ -441,6 +441,304 @@ func TestCreateStatus(t *testing.T) {
441441 }
442442}
443443
444+ func TestCreateStatusPipelineIDCaching (t * testing.T ) {
445+ ctx , _ := rtesting .SetupFakeContext (t )
446+ log , _ := logger .GetLogger ()
447+ stdata , _ := testclient .SeedTestData (t , ctx , testclient.Data {})
448+ run := & params.Run {
449+ Clients : clients.Clients {
450+ Kube : stdata .Kube ,
451+ Log : log ,
452+ },
453+ }
454+
455+ client , mux , tearDown := thelp .Setup (t )
456+ defer tearDown ()
457+
458+ v := & Provider {
459+ run : params .New (),
460+ Logger : log ,
461+ pacInfo : & info.PacOpts {
462+ Settings : settings.Settings {
463+ ApplicationName : settings .PACApplicationNameDefaultValue ,
464+ },
465+ },
466+ eventEmitter : events .NewEventEmitter (run .Clients .Kube , log ),
467+ }
468+ v .SetGitLabClient (client )
469+
470+ event := & info.Event {
471+ TriggerTarget : "pull_request" ,
472+ SourceProjectID : 500 ,
473+ TargetProjectID : 500 ,
474+ SHA : "deadbeef" ,
475+ PullRequestNumber : 1 ,
476+ }
477+
478+ var callCount int
479+ mux .HandleFunc (fmt .Sprintf ("/projects/%d/statuses/%s" , event .SourceProjectID , event .SHA ),
480+ func (rw http.ResponseWriter , r * http.Request ) {
481+ callCount ++
482+ var reqBody map [string ]any
483+ if err := json .NewDecoder (r .Body ).Decode (& reqBody ); err != nil {
484+ t .Fatalf ("failed to decode request body: %v" , err )
485+ }
486+ // First call should NOT include pipeline_id
487+ if callCount == 1 {
488+ _ , hasPipelineID := reqBody ["pipeline_id" ]
489+ assert .Assert (t , ! hasPipelineID ,
490+ "first call should not have pipeline_id" )
491+ }
492+ // Subsequent calls MUST include the cached pipeline_id
493+ if callCount > 1 {
494+ pid , ok := reqBody ["pipeline_id" ]
495+ assert .Assert (t , ok , "subsequent calls must include pipeline_id" )
496+ pidFloat , ok := pid .(float64 )
497+ assert .Assert (t , ok , "pipeline_id must be a number" )
498+ assert .Equal (t , int64 (pidFloat ), int64 (9999 ),
499+ "pipeline_id must match cached value" )
500+ }
501+ rw .WriteHeader (http .StatusCreated )
502+ fmt .Fprint (rw , `{"id": 1, "pipeline_id": 9999}` )
503+ })
504+
505+ // First call — should cache pipeline_id from response
506+ statusOpts := providerstatus.StatusOpts {
507+ Conclusion : "success" ,
508+ OriginalPipelineRunName : "pr-1" ,
509+ }
510+ err := v .CreateStatus (ctx , event , statusOpts )
511+ assert .NilError (t , err )
512+ assert .Equal (t , callCount , 1 )
513+
514+ // Verify the pipeline ID was cached
515+ key := pipelineIDKey (event .SourceProjectID , event .SHA )
516+ assert .Equal (t , v .commitStatusPipelineIDs [key ], int64 (9999 ))
517+
518+ // Second call — should reuse the cached pipeline_id
519+ statusOpts .OriginalPipelineRunName = "pr-2"
520+ err = v .CreateStatus (ctx , event , statusOpts )
521+ assert .NilError (t , err )
522+ assert .Equal (t , callCount , 2 )
523+ }
524+
525+ func TestCreateStatusPipelineIDCachingTargetFallback (t * testing.T ) {
526+ ctx , _ := rtesting .SetupFakeContext (t )
527+ log , _ := logger .GetLogger ()
528+ stdata , _ := testclient .SeedTestData (t , ctx , testclient.Data {})
529+ run := & params.Run {
530+ Clients : clients.Clients {
531+ Kube : stdata .Kube ,
532+ Log : log ,
533+ },
534+ }
535+
536+ client , mux , tearDown := thelp .Setup (t )
537+ defer tearDown ()
538+
539+ v := & Provider {
540+ targetProjectID : 600 ,
541+ run : params .New (),
542+ Logger : log ,
543+ pacInfo : & info.PacOpts {
544+ Settings : settings.Settings {
545+ ApplicationName : settings .PACApplicationNameDefaultValue ,
546+ },
547+ },
548+ eventEmitter : events .NewEventEmitter (run .Clients .Kube , log ),
549+ }
550+ v .SetGitLabClient (client )
551+
552+ event := & info.Event {
553+ TriggerTarget : "pull_request" ,
554+ SourceProjectID : 404 , // will fail
555+ TargetProjectID : 600 ,
556+ SHA : "aabb1122" ,
557+ PullRequestNumber : 1 ,
558+ }
559+
560+ // Source returns 404
561+ mux .HandleFunc (fmt .Sprintf ("/projects/%d/statuses/%s" , event .SourceProjectID , event .SHA ),
562+ func (rw http.ResponseWriter , _ * http.Request ) {
563+ rw .WriteHeader (http .StatusNotFound )
564+ fmt .Fprint (rw , `{"message": "404 Not Found"}` )
565+ })
566+
567+ var targetCallCount int
568+ mux .HandleFunc (fmt .Sprintf ("/projects/%d/statuses/%s" , event .TargetProjectID , event .SHA ),
569+ func (rw http.ResponseWriter , r * http.Request ) {
570+ targetCallCount ++
571+ var reqBody map [string ]any
572+ if err := json .NewDecoder (r .Body ).Decode (& reqBody ); err != nil {
573+ t .Fatalf ("failed to decode request body: %v" , err )
574+ }
575+ if targetCallCount == 1 {
576+ _ , hasPipelineID := reqBody ["pipeline_id" ]
577+ assert .Assert (t , ! hasPipelineID ,
578+ "first target call should not have pipeline_id" )
579+ }
580+ if targetCallCount > 1 {
581+ pid , ok := reqBody ["pipeline_id" ]
582+ assert .Assert (t , ok , "subsequent target calls must include pipeline_id" )
583+ pidFloat , ok := pid .(float64 )
584+ assert .Assert (t , ok , "pipeline_id must be a number" )
585+ assert .Equal (t , int64 (pidFloat ), int64 (7777 ))
586+ }
587+ rw .WriteHeader (http .StatusCreated )
588+ fmt .Fprint (rw , `{"id": 1, "pipeline_id": 7777}` )
589+ })
590+
591+ statusOpts := providerstatus.StatusOpts {
592+ Conclusion : "success" ,
593+ OriginalPipelineRunName : "pr-1" ,
594+ }
595+ err := v .CreateStatus (ctx , event , statusOpts )
596+ assert .NilError (t , err )
597+
598+ key := pipelineIDKey (event .TargetProjectID , event .SHA )
599+ assert .Equal (t , v .commitStatusPipelineIDs [key ], int64 (7777 ))
600+
601+ // Second call should reuse target's cached pipeline_id
602+ statusOpts .OriginalPipelineRunName = "pr-2"
603+ err = v .CreateStatus (ctx , event , statusOpts )
604+ assert .NilError (t , err )
605+ assert .Equal (t , targetCallCount , 2 )
606+ }
607+
608+ func TestCreateStatusPipelineIDZeroNotCached (t * testing.T ) {
609+ ctx , _ := rtesting .SetupFakeContext (t )
610+ log , _ := logger .GetLogger ()
611+ stdata , _ := testclient .SeedTestData (t , ctx , testclient.Data {})
612+ run := & params.Run {
613+ Clients : clients.Clients {
614+ Kube : stdata .Kube ,
615+ Log : log ,
616+ },
617+ }
618+
619+ client , mux , tearDown := thelp .Setup (t )
620+ defer tearDown ()
621+
622+ v := & Provider {
623+ run : params .New (),
624+ Logger : log ,
625+ pacInfo : & info.PacOpts {
626+ Settings : settings.Settings {
627+ ApplicationName : settings .PACApplicationNameDefaultValue ,
628+ },
629+ },
630+ eventEmitter : events .NewEventEmitter (run .Clients .Kube , log ),
631+ }
632+ v .SetGitLabClient (client )
633+
634+ event := & info.Event {
635+ TriggerTarget : "pull_request" ,
636+ SourceProjectID : 700 ,
637+ TargetProjectID : 700 ,
638+ SHA : "cafe0000" ,
639+ PullRequestNumber : 1 ,
640+ }
641+
642+ mux .HandleFunc (fmt .Sprintf ("/projects/%d/statuses/%s" , event .SourceProjectID , event .SHA ),
643+ func (rw http.ResponseWriter , r * http.Request ) {
644+ var reqBody map [string ]any
645+ if err := json .NewDecoder (r .Body ).Decode (& reqBody ); err != nil {
646+ t .Fatalf ("failed to decode request body: %v" , err )
647+ }
648+ // pipeline_id should never be sent since response always returns 0
649+ _ , hasPipelineID := reqBody ["pipeline_id" ]
650+ assert .Assert (t , ! hasPipelineID ,
651+ "pipeline_id should not be sent when cached value is 0" )
652+ rw .WriteHeader (http .StatusCreated )
653+ // Response with pipeline_id 0 — should not be cached
654+ fmt .Fprint (rw , `{"id": 1, "pipeline_id": 0}` )
655+ })
656+
657+ statusOpts := providerstatus.StatusOpts {
658+ Conclusion : "success" ,
659+ OriginalPipelineRunName : "pr-1" ,
660+ }
661+ err := v .CreateStatus (ctx , event , statusOpts )
662+ assert .NilError (t , err )
663+ assert .Assert (t , v .commitStatusPipelineIDs == nil ,
664+ "pipeline ID 0 should not be cached" )
665+
666+ // Second call — still no pipeline_id
667+ err = v .CreateStatus (ctx , event , statusOpts )
668+ assert .NilError (t , err )
669+ }
670+
671+ func TestCreateStatusPipelineIDSeparateSHAs (t * testing.T ) {
672+ ctx , _ := rtesting .SetupFakeContext (t )
673+ log , _ := logger .GetLogger ()
674+ stdata , _ := testclient .SeedTestData (t , ctx , testclient.Data {})
675+ run := & params.Run {
676+ Clients : clients.Clients {
677+ Kube : stdata .Kube ,
678+ Log : log ,
679+ },
680+ }
681+
682+ client , mux , tearDown := thelp .Setup (t )
683+ defer tearDown ()
684+
685+ v := & Provider {
686+ run : params .New (),
687+ Logger : log ,
688+ pacInfo : & info.PacOpts {
689+ Settings : settings.Settings {
690+ ApplicationName : settings .PACApplicationNameDefaultValue ,
691+ },
692+ },
693+ eventEmitter : events .NewEventEmitter (run .Clients .Kube , log ),
694+ }
695+ v .SetGitLabClient (client )
696+
697+ sha1 := "sha1aaaa"
698+ sha2 := "sha2bbbb"
699+ projectID := int64 (800 )
700+
701+ mux .HandleFunc (fmt .Sprintf ("/projects/%d/statuses/%s" , projectID , sha1 ),
702+ func (rw http.ResponseWriter , _ * http.Request ) {
703+ rw .WriteHeader (http .StatusCreated )
704+ fmt .Fprint (rw , `{"id": 1, "pipeline_id": 1111}` )
705+ })
706+ mux .HandleFunc (fmt .Sprintf ("/projects/%d/statuses/%s" , projectID , sha2 ),
707+ func (rw http.ResponseWriter , _ * http.Request ) {
708+ rw .WriteHeader (http .StatusCreated )
709+ fmt .Fprint (rw , `{"id": 2, "pipeline_id": 2222}` )
710+ })
711+
712+ event1 := & info.Event {
713+ TriggerTarget : "pull_request" ,
714+ SourceProjectID : projectID ,
715+ TargetProjectID : projectID ,
716+ SHA : sha1 ,
717+ PullRequestNumber : 1 ,
718+ }
719+ event2 := & info.Event {
720+ TriggerTarget : "pull_request" ,
721+ SourceProjectID : projectID ,
722+ TargetProjectID : projectID ,
723+ SHA : sha2 ,
724+ PullRequestNumber : 2 ,
725+ }
726+
727+ statusOpts := providerstatus.StatusOpts {
728+ Conclusion : "success" ,
729+ OriginalPipelineRunName : "pr-1" ,
730+ }
731+
732+ err := v .CreateStatus (ctx , event1 , statusOpts )
733+ assert .NilError (t , err )
734+ err = v .CreateStatus (ctx , event2 , statusOpts )
735+ assert .NilError (t , err )
736+
737+ // Each SHA must have its own cached pipeline ID
738+ assert .Equal (t , v .commitStatusPipelineIDs [pipelineIDKey (projectID , sha1 )], int64 (1111 ))
739+ assert .Equal (t , v .commitStatusPipelineIDs [pipelineIDKey (projectID , sha2 )], int64 (2222 ))
740+ }
741+
444742func TestGetCommitInfo (t * testing.T ) {
445743 tests := []struct {
446744 name string
0 commit comments