package auto import ( "context" "os" "path/filepath" "testing" git "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/object" "github.com/stretchr/testify/assert" ) // This takes the unusual step of testing an unexported func. The rationale is to be able to test // git code in isolation; testing the user of the unexported func (NewLocalWorkspace) drags in lots // of other factors. func TestGitClone(t *testing.T) { t.Parallel() // This makes a git repo to clone from, so to avoid relying on something at GitHub that could // change or be inaccessible. tmpDir := t.TempDir() originDir := filepath.Join(tmpDir, "origin") origin, err := git.PlainInit(originDir, false) assert.NoError(t, err) w, err := origin.Worktree() assert.NoError(t, err) nondefaultHead, err := w.Commit("nondefault branch", &git.CommitOptions{ Author: &object.Signature{ Name: "testo", Email: "testo@example.com", }, AllowEmptyCommits: true, }) assert.NoError(t, err) // The following sets up some tags and branches: with `default` becoming the "default" branch // when cloning, since it's left as the HEAD of the repo. assert.NoError(t, w.Checkout(&git.CheckoutOptions{ Branch: plumbing.NewBranchReferenceName("nondefault"), Create: true, })) // tag the nondefault head so we can test getting a tag too _, err = origin.CreateTag("v0.0.1", nondefaultHead, nil) assert.NoError(t, err) // make a branch with slashes in it, so that can be tested too assert.NoError(t, w.Checkout(&git.CheckoutOptions{ Branch: plumbing.NewBranchReferenceName("branch/with/slashes"), Create: true, })) assert.NoError(t, w.Checkout(&git.CheckoutOptions{ Branch: plumbing.NewBranchReferenceName("default"), Create: true, })) defaultHead, err := w.Commit("default branch", &git.CommitOptions{ Author: &object.Signature{ Name: "testo", Email: "testo@example.com", }, AllowEmptyCommits: true, }) assert.NoError(t, err) type testcase struct { branchName string commitHash string testName string // use when supplying a hash, for a stable name expectedHead plumbing.Hash expectedError string } for _, tc := range []testcase{ {branchName: "default", expectedHead: defaultHead}, {branchName: "nondefault", expectedHead: nondefaultHead}, {branchName: "branch/with/slashes", expectedHead: nondefaultHead}, // https://github.com/pulumi/pulumi-kubernetes-operator/issues/103#issuecomment-1107891475 // advises using `refs/heads/<default>` for the default, and `refs/remotes/origin/<branch>` // for a non-default branch -- so we can expect all these varieties to be in use. {branchName: "refs/heads/default", expectedHead: defaultHead}, {branchName: "refs/heads/nondefault", expectedHead: nondefaultHead}, {branchName: "refs/heads/branch/with/slashes", expectedHead: nondefaultHead}, {branchName: "refs/remotes/origin/default", expectedHead: defaultHead}, {branchName: "refs/remotes/origin/nondefault", expectedHead: nondefaultHead}, {branchName: "refs/remotes/origin/branch/with/slashes", expectedHead: nondefaultHead}, // try the special tag case {branchName: "refs/tags/v0.0.1", expectedHead: nondefaultHead}, // ask specifically for the commit hash {testName: "head of default as hash", commitHash: defaultHead.String(), expectedHead: defaultHead}, {testName: "head of nondefault as hash", commitHash: nondefaultHead.String(), expectedHead: nondefaultHead}, } { tc := tc if tc.testName == "" { tc.testName = tc.branchName } t.Run(tc.testName, func(t *testing.T) { t.Parallel() repo := &GitRepo{ URL: originDir, Branch: tc.branchName, CommitHash: tc.commitHash, } tmp, err := os.MkdirTemp(tmpDir, "testcase") // i.e., under the tmp dir from earlier assert.NoError(t, err) _, err = setupGitRepo(context.Background(), tmp, repo) assert.NoError(t, err) r, err := git.PlainOpen(tmp) assert.NoError(t, err) head, err := r.Head() assert.NoError(t, err) assert.Equal(t, tc.expectedHead, head.Hash()) }) } // test that these result in errors for _, tc := range []testcase{ { testName: "simple branch doesn't exist", branchName: "doesnotexist", expectedError: "unable to clone repo: reference not found", }, { testName: "full branch doesn't exist", branchName: "refs/heads/doesnotexist", expectedError: "unable to clone repo: reference not found", }, { testName: "malformed branch name", branchName: "refs/notathing/default", expectedError: "unable to clone repo: reference not found", }, { testName: "simple tag name won't work", branchName: "v1.0.0", expectedError: "unable to clone repo: reference not found", }, { testName: "wrong remote", branchName: "refs/remotes/upstream/default", expectedError: "a remote ref must begin with 'refs/remote/origin/', " + "but got \"refs/remotes/upstream/default\"", }, } { tc := tc if tc.testName == "" { tc.testName = tc.branchName } t.Run(tc.testName, func(t *testing.T) { t.Parallel() repo := &GitRepo{ URL: originDir, Branch: tc.branchName, CommitHash: tc.commitHash, } tmp, err := os.MkdirTemp(tmpDir, "testcase") // i.e., under the tmp dir from earlier assert.NoError(t, err) _, err = setupGitRepo(context.Background(), tmp, repo) assert.EqualError(t, err, tc.expectedError) }) } }