Add git.CheckoutStrategy SemVer checkout tests

Adds tests for git.CheckoutStrategy to check if both the git
implementations follow the same SemVer tag selection rules.

Signed-off-by: Sunny <darkowlzz@protonmail.com>
This commit is contained in:
Sunny 2021-10-26 22:59:05 +05:30
parent 99428f593e
commit 562af6d658
1 changed files with 206 additions and 0 deletions

View File

@ -18,14 +18,20 @@ package strategy
import ( import (
"context" "context"
"errors"
"net/url" "net/url"
"os" "os"
"path/filepath"
"strings" "strings"
"testing" "testing"
"time" "time"
"github.com/fluxcd/pkg/gittestserver" "github.com/fluxcd/pkg/gittestserver"
"github.com/fluxcd/pkg/ssh" "github.com/fluxcd/pkg/ssh"
extgogit "github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
"github.com/fluxcd/source-controller/pkg/git" "github.com/fluxcd/source-controller/pkg/git"
@ -198,3 +204,203 @@ func getSSHRepoURL(sshAddress, repoPath string) string {
sshURL := strings.Replace(sshAddress, "127.0.0.1", "localhost", 1) sshURL := strings.Replace(sshAddress, "127.0.0.1", "localhost", 1)
return sshURL + "/" + repoPath return sshURL + "/" + repoPath
} }
func TestCheckoutStrategyForImplementation_SemVerCheckout(t *testing.T) {
g := NewWithT(t)
gitImpls := []git.Implementation{gogit.Implementation, libgit2.Implementation}
// Setup git server and repo.
gitServer, err := gittestserver.NewTempGitServer()
g.Expect(err).ToNot(HaveOccurred())
defer os.RemoveAll(gitServer.Root())
username := "test-user"
password := "test-password"
gitServer.Auth(username, password)
gitServer.KeyDir(gitServer.Root())
g.Expect(gitServer.StartHTTP()).ToNot(HaveOccurred())
defer gitServer.StopHTTP()
repoPath := "bar/test-reponame"
err = gitServer.InitRepo("testdata/repo1", "master", repoPath)
g.Expect(err).ToNot(HaveOccurred())
repoURL := gitServer.HTTPAddressWithCredentials() + "/" + repoPath
authOpts := &git.AuthOptions{
Transport: git.HTTP,
Username: username,
Password: password,
}
// Create test tags in the repo.
now := time.Now()
tags := []struct {
tag string
annotated bool
commitTime time.Time
tagTime time.Time
}{
{
tag: "v0.0.1",
annotated: false,
commitTime: now,
},
{
tag: "v0.1.0+build-1",
annotated: true,
commitTime: now.Add(10 * time.Minute),
tagTime: now.Add(2 * time.Hour), // This should be ignored during TS comparisons
},
{
tag: "v0.1.0+build-2",
annotated: false,
commitTime: now.Add(30 * time.Minute),
},
{
tag: "v0.1.0+build-3",
annotated: true,
commitTime: now.Add(1 * time.Hour),
tagTime: now.Add(1 * time.Hour), // This should be ignored during TS comparisons
},
{
tag: "0.2.0",
annotated: true,
commitTime: now,
tagTime: now,
},
}
// Clone the repo locally.
cloneDir, err := os.MkdirTemp("", "test-clone")
g.Expect(err).ToNot(HaveOccurred())
defer os.RemoveAll(cloneDir)
repo, err := extgogit.PlainClone(cloneDir, false, &extgogit.CloneOptions{
URL: repoURL,
})
g.Expect(err).ToNot(HaveOccurred())
// Create commits and tags.
// Keep a record of all the tags and commit refs.
refs := make(map[string]string, len(tags))
for _, tt := range tags {
ref, err := commitFile(repo, "tag", tt.tag, tt.commitTime)
g.Expect(err).ToNot(HaveOccurred())
_, err = tag(repo, ref, tt.annotated, tt.tag, tt.tagTime)
g.Expect(err).ToNot(HaveOccurred())
refs[tt.tag] = ref.String()
}
// Push everything.
err = repo.Push(&extgogit.PushOptions{
RefSpecs: []config.RefSpec{"refs/*:refs/*"},
})
g.Expect(err).ToNot(HaveOccurred())
// Test cases.
type testCase struct {
name string
constraint string
expectErr error
expectTag string
}
tests := []testCase{
{
name: "Orders by SemVer",
constraint: ">0.1.0",
expectTag: "0.2.0",
},
{
name: "Orders by SemVer and timestamp",
constraint: "<0.2.0",
expectTag: "v0.1.0+build-3",
},
{
name: "Errors without match",
constraint: ">=1.0.0",
expectErr: errors.New("no match found for semver: >=1.0.0"),
},
}
testFunc := func(tt testCase, impl git.Implementation) func(t *testing.T) {
return func(t *testing.T) {
g := NewWithT(t)
// Get the checkout strategy.
checkoutOpts := git.CheckoutOptions{
SemVer: tt.constraint,
}
checkoutStrategy, err := CheckoutStrategyForImplementation(context.TODO(), impl, checkoutOpts)
g.Expect(err).ToNot(HaveOccurred())
// Checkout and verify.
tmpDir, err := os.MkdirTemp("", "test-checkout")
g.Expect(err).ToNot(HaveOccurred())
defer os.RemoveAll(tmpDir)
cc, err := checkoutStrategy.Checkout(context.TODO(), tmpDir, repoURL, authOpts)
if tt.expectErr != nil {
g.Expect(err).To(Equal(tt.expectErr))
g.Expect(cc).To(BeNil())
return
}
g.Expect(err).ToNot(HaveOccurred())
g.Expect(cc.String()).To(Equal(tt.expectTag + "/" + refs[tt.expectTag]))
g.Expect(filepath.Join(tmpDir, "tag")).To(BeARegularFile())
g.Expect(os.ReadFile(filepath.Join(tmpDir, "tag"))).To(BeEquivalentTo(tt.expectTag))
}
}
// Run the test cases against the git implementations.
for _, gitImpl := range gitImpls {
for _, tt := range tests {
t.Run(string(gitImpl)+"_"+tt.name, testFunc(tt, gitImpl))
}
}
}
func commitFile(repo *extgogit.Repository, path, content string, time time.Time) (plumbing.Hash, error) {
wt, err := repo.Worktree()
if err != nil {
return plumbing.Hash{}, err
}
f, err := wt.Filesystem.Create(path)
if err != nil {
return plumbing.Hash{}, err
}
if _, err := f.Write([]byte(content)); err != nil {
if ferr := f.Close(); ferr != nil {
return plumbing.Hash{}, ferr
}
return plumbing.Hash{}, err
}
if err := f.Close(); err != nil {
return plumbing.Hash{}, err
}
if _, err := wt.Add(path); err != nil {
return plumbing.Hash{}, err
}
return wt.Commit("Adding: "+path, &extgogit.CommitOptions{
Author: mockSignature(time),
Committer: mockSignature(time),
})
}
func tag(repo *extgogit.Repository, commit plumbing.Hash, annotated bool, tag string, time time.Time) (*plumbing.Reference, error) {
var opts *extgogit.CreateTagOptions
if annotated {
opts = &extgogit.CreateTagOptions{
Tagger: mockSignature(time),
Message: "Annotated tag for: " + tag,
}
}
return repo.CreateTag(tag, commit, opts)
}
func mockSignature(time time.Time) *object.Signature {
return &object.Signature{
Name: "Jane Doe",
Email: "jane@example.com",
When: time,
}
}