@@ -19,6 +19,7 @@ import (
1919 "errors"
2020 "time"
2121
22+ kptfilev1 "github.com/kptdev/kpt/api/kptfile/v1"
2223 porchapi "github.com/kptdev/porch/api/porch/v1alpha1"
2324 configapi "github.com/kptdev/porch/api/porchconfig/v1alpha1"
2425 cachetypes "github.com/kptdev/porch/pkg/cache/types"
@@ -27,6 +28,7 @@ import (
2728 externalrepotypes "github.com/kptdev/porch/pkg/externalrepo/types"
2829 "github.com/kptdev/porch/pkg/repository"
2930 mockcachetypes "github.com/kptdev/porch/test/mockery/mocks/porch/pkg/cache/types"
31+ mockrepo "github.com/kptdev/porch/test/mockery/mocks/porch/pkg/repository"
3032 "github.com/stretchr/testify/mock"
3133 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3234)
@@ -741,3 +743,94 @@ func (t *DbTestSuite) TestBackfillUpstreamRefName() {
741743
742744 t .deleteTestRepo (dbRepo .Key ())
743745}
746+
747+ func (t * DbTestSuite ) TestDBPackageRevisionPublishWithPushDraftsToGit () {
748+ mockCache := mockcachetypes .NewMockCache (t .T ())
749+ cachetypes .CacheInstance = mockCache
750+ externalrepo .ExternalRepoInUnitTestMode = true
751+
752+ ctx := t .Context ()
753+ namespace := "my-ns"
754+ repoName := "publish-push-drafts-repo"
755+
756+ testRepo := t .createTestRepo (namespace , repoName )
757+ testRepo .spec = & configapi.Repository {
758+ Spec : configapi.RepositorySpec {
759+ Git : & configapi.GitRepository {
760+ Repo : "https://aurl/repo.git" ,
761+ },
762+ },
763+ }
764+ mockCache .EXPECT ().GetRepository (mock .Anything ).Return (testRepo ).Maybe ()
765+
766+ // Stand in for the git repository. Using a mock rather than the fake external repo means
767+ // every call made to git is asserted, so an unexpected second close fails the test.
768+ extRepo := mockrepo .NewMockRepository (t .T ())
769+ extRepo .EXPECT ().Key ().Return (repository.RepositoryKey {Namespace : namespace , Name : repoName }).Maybe ()
770+
771+ testRepo .externalRepo = extRepo
772+ testRepo .pushDraftsToGit = true
773+ testRepo .gitPRCache = make (map [string ]repository.PackageRevision )
774+
775+ // Create: a git draft is opened and then closed, yielding the cached git PR.
776+ initialGitDraft := mockrepo .NewMockPackageRevisionDraft (t .T ())
777+ cachedGitPR := mockrepo .NewMockPackageRevision (t .T ())
778+ extRepo .EXPECT ().CreatePackageRevisionDraft (mock .Anything , mock .Anything ).Return (initialGitDraft , nil ).Once ()
779+ extRepo .EXPECT ().ClosePackageRevisionDraft (mock .Anything , initialGitDraft , 0 ).Return (cachedGitPR , nil ).Once ()
780+
781+ newPRDef := porchapi.PackageRevision {
782+ Spec : porchapi.PackageRevisionSpec {
783+ RepositoryName : repoName ,
784+ PackageName : "my-package" ,
785+ WorkspaceName : "my-workspace" ,
786+ Lifecycle : porchapi .PackageRevisionLifecycleDraft ,
787+ },
788+ }
789+
790+ prDraft , err := testRepo .CreatePackageRevisionDraft (ctx , & newPRDef )
791+ t .Require ().NoError (err )
792+
793+ dbPR , err := testRepo .ClosePackageRevisionDraft (ctx , prDraft , 0 )
794+ t .Require ().NoError (err )
795+ t .Require ().Nil (dbPR .(* dbPackageRevision ).gitPRDraft , "closing a draft must release the git draft handle" )
796+
797+ // Propose.
798+ err = dbPR .UpdateLifecycle (ctx , porchapi .PackageRevisionLifecycleProposed )
799+ t .Require ().NoError (err )
800+
801+ dbPR , err = testRepo .ClosePackageRevisionDraft (ctx , dbPR .(repository.PackageRevisionDraft ), 0 )
802+ t .Require ().NoError (err )
803+
804+ // Approve. dbRepository.UpdatePackageRevision reopens the cached git PR as a draft, and
805+ // publishPR opens and closes a second one with the real revision number.
806+ staleGitDraft := mockrepo .NewMockPackageRevisionDraft (t .T ())
807+ publishGitDraft := mockrepo .NewMockPackageRevisionDraft (t .T ())
808+ publishedGitPR := mockrepo .NewMockPackageRevision (t .T ())
809+
810+ extRepo .EXPECT ().UpdatePackageRevision (mock .Anything , cachedGitPR ).Return (staleGitDraft , nil ).Once ()
811+ extRepo .EXPECT ().UpdatePackageRevision (mock .Anything , cachedGitPR ).Return (publishGitDraft , nil ).Once ()
812+ publishGitDraft .EXPECT ().UpdateLifecycle (mock .Anything , porchapi .PackageRevisionLifecyclePublished ).Return (nil ).Once ()
813+ // The revision number, never 0, is what reaches git for a Published draft.
814+ extRepo .EXPECT ().ClosePackageRevisionDraft (mock .Anything , publishGitDraft , 1 ).Return (publishedGitPR , nil ).Once ()
815+ publishedGitPR .EXPECT ().GetLock (mock .Anything ).Return (kptfilev1.Upstream {}, kptfilev1.Locator {}, nil ).Once ()
816+
817+ approveDraft , err := testRepo .UpdatePackageRevision (ctx , dbPR )
818+ t .Require ().NoError (err )
819+ t .Require ().Equal (staleGitDraft , approveDraft .(* dbPackageRevision ).gitPRDraft )
820+
821+ err = approveDraft .UpdateLifecycle (ctx , porchapi .PackageRevisionLifecyclePublished )
822+ t .Require ().NoError (err )
823+ t .Require ().Nil (approveDraft .(* dbPackageRevision ).gitPRDraft ,
824+ "publishing must release the stale git draft handle so it is not closed with version 0" )
825+
826+ // No further git calls are expected here: staleGitDraft is never closed.
827+ publishedPR , err := testRepo .ClosePackageRevisionDraft (ctx , approveDraft , 0 )
828+ t .Require ().NoError (err )
829+ t .Require ().Equal (1 , publishedPR .Key ().Revision )
830+ t .Require ().Equal (porchapi .PackageRevisionLifecyclePublished , publishedPR .Lifecycle (ctx ))
831+
832+ // Other tests in this suite assert on database-wide package revision counts, so drop
833+ // everything this test created. Close only removes cached packages, not external ones.
834+ extRepo .EXPECT ().Close (mock .Anything ).Return (nil ).Once ()
835+ t .Require ().NoError (testRepo .Close (ctx ))
836+ }
0 commit comments