pulumi/sdk/go/auto/git_test.go

188 lines
6.0 KiB
Go

// Copyright 2022-2024, Pulumi Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
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)
})
}
}