Skip to content

Commit

Permalink
chore(updater): increase test coverage (#179)
Browse files Browse the repository at this point in the history
  • Loading branch information
shini4i authored Sep 1, 2023
1 parent 72da12c commit 3785f83
Show file tree
Hide file tree
Showing 8 changed files with 315 additions and 7 deletions.
2 changes: 2 additions & 0 deletions .codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ignore:
- "pkg/updater/interfaces.go"
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ argo-watcher
# dynamicly generated files
cmd/argo-watcher/docs
cmd/argo-watcher/mock
pkg/updater/mock

# goreleaser
dist/
4 changes: 1 addition & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,10 @@ docs: ## Generate swagger docs
.PHONY: mocks
mocks:
@echo "===> Generating mocks"
# generate API mock
@mockgen --source=cmd/argo-watcher/argo_api.go --destination=cmd/argo-watcher/mock/argo_api.go --package=mock
# generate State mock
@mockgen --source=cmd/argo-watcher/state/state.go --destination=cmd/argo-watcher/mock/state.go --package=mock
# generate Metrics mock
@mockgen --source=cmd/argo-watcher/metrics.go --destination=cmd/argo-watcher/mock/metrics.go --package=mock
@mockgen --source=pkg/updater/interfaces.go --destination=pkg/updater/mock/interfaces.go --package=mock

.PHONY: bootstrap
bootstrap: ## Bootstrap docker compose setup
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1958,8 +1958,6 @@ gorm.io/driver/sqlserver v1.4.1/go.mod h1:DJ4P+MeZbc5rvY58PnmN1Lnyvb5gw5NPzGshHD
gorm.io/gorm v1.20.12/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
gorm.io/gorm v1.21.4/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
gorm.io/gorm v1.25.2 h1:gs1o6Vsa+oVKG/a9ElL3XgyGfghFfkKA2SInQaCyMho=
gorm.io/gorm v1.25.2/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
gorm.io/gorm v1.25.4 h1:iyNd8fNAe8W9dvtlgeRI5zSVZPsq3OpcTu37cYcpCmw=
gorm.io/gorm v1.25.4/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
Expand Down
2 changes: 2 additions & 0 deletions internal/models/argo.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,8 @@ func (app *Application) UpdateGitImageTag(task *Task) error {
RepoURL: app.Spec.Source.RepoURL,
BranchName: app.Spec.Source.TargetRevision,
Path: app.Spec.Source.Path,

GitHandler: updater.GitClient{},
}

if err := git.Clone(); err != nil {
Expand Down
23 changes: 23 additions & 0 deletions pkg/updater/interfaces.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package updater

import (
"github.com/go-git/go-billy/v5"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing/transport/ssh"
"github.com/go-git/go-git/v5/storage"
)

type GitHandler interface {
Clone(s storage.Storer, worktree billy.Filesystem, o *git.CloneOptions) (*git.Repository, error)
NewPublicKeysFromFile(user, path, passphrase string) (*ssh.PublicKeys, error)
}

type GitClient struct{}

func (GitClient) Clone(s storage.Storer, worktree billy.Filesystem, o *git.CloneOptions) (*git.Repository, error) {
return git.Clone(s, worktree, o)
}

func (GitClient) NewPublicKeysFromFile(user, path, passphrase string) (*ssh.PublicKeys, error) {
return ssh.NewPublicKeysFromFile(user, path, passphrase)
}
7 changes: 5 additions & 2 deletions pkg/updater/updater.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,21 +42,24 @@ type GitRepo struct {
fs billy.Filesystem
localRepo *git.Repository
sshAuth *ssh.PublicKeys

GitHandler GitHandler
}

func (repo *GitRepo) Clone() error {
var err error

repo.fs = memfs.New()

if repo.sshAuth, err = ssh.NewPublicKeysFromFile("git", sshKeyPath, sshKeyPass); err != nil {
if repo.sshAuth, err = repo.GitHandler.NewPublicKeysFromFile("git", sshKeyPath, sshKeyPass); err != nil {
return err
}

repo.localRepo, err = git.Clone(memory.NewStorage(), repo.fs, &git.CloneOptions{
repo.localRepo, err = repo.GitHandler.Clone(memory.NewStorage(), repo.fs, &git.CloneOptions{
URL: repo.RepoURL,
ReferenceName: plumbing.ReferenceName("refs/heads/" + repo.BranchName),
SingleBranch: true,
Depth: 1, // This is needed to avoid fetching the entire history, which is not needed in this case
Auth: repo.sshAuth,
})

Expand Down
281 changes: 281 additions & 0 deletions pkg/updater/updater_test.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,230 @@
package updater

import (
"errors"
"github.com/go-git/go-billy/v5"
"github.com/go-git/go-billy/v5/memfs"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/go-git/go-git/v5/plumbing/transport/ssh"
"github.com/go-git/go-git/v5/storage/memory"
"github.com/shini4i/argo-watcher/pkg/updater/mock"
"github.com/stretchr/testify/assert"
"go.uber.org/mock/gomock"
"gopkg.in/yaml.v2"
"strings"
"testing"
"time"
)

func TestGitRepoClone(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

mockGitHandler := mock.NewMockGitHandler(ctrl)

tests := []struct {
name string
mockSSH func()
expected error
}{
{
name: "successful clone",
mockSSH: func() {
mockGitHandler.EXPECT().NewPublicKeysFromFile("git", sshKeyPath, sshKeyPass).Return(&ssh.PublicKeys{}, nil)
mockGitHandler.EXPECT().Clone(memory.NewStorage(), memfs.New(), &git.CloneOptions{
URL: "mockRepoURL",
ReferenceName: "refs/heads/mockBranch",
SingleBranch: true,
Depth: 1,
Auth: &ssh.PublicKeys{},
}).Return(&git.Repository{}, nil)
},
expected: nil,
},
{
name: "failed NewPublicKeysFromFile",
mockSSH: func() {
mockGitHandler.EXPECT().NewPublicKeysFromFile("git", sshKeyPath, sshKeyPass).Return(nil, errors.New("failed to fetch keys"))
},
expected: errors.New("failed to fetch keys"),
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.mockSSH()

gitRepo := GitRepo{
RepoURL: "mockRepoURL",
BranchName: "mockBranch",
GitHandler: mockGitHandler,
}

err := gitRepo.Clone()

if tt.expected == nil {
assert.NoError(t, err, "Expected no error")
} else {
assert.EqualError(t, err, tt.expected.Error(), "Error mismatch")
}
})
}
}

func TestGetFileContent(t *testing.T) {
// 1. Setup an in-memory file system using billy
fs := memfs.New()
content := "Hello, World!"
fileName := "test.txt"

// 2. Create a test file in that filesystem
file, err := fs.Create(fileName)
assert.NoError(t, err)
_, err = file.Write([]byte(content))
assert.NoError(t, err)
err = file.Close()
assert.NoError(t, err)

// 3. Create a GitRepo instance using the in-memory filesystem
repo := &GitRepo{
fs: fs,
}

t.Run("Successfully read content", func(t *testing.T) {
readContent, err := repo.getFileContent(fileName)
assert.NoError(t, err)
assert.Equal(t, content, strings.TrimSpace(string(readContent)))
})

t.Run("Error on non-existent file", func(t *testing.T) {
_, err := repo.getFileContent("non-existent.txt")
assert.Error(t, err)
})
}

func TestMergeOverrideFileContent(t *testing.T) {
fs := memfs.New()
repo := &GitRepo{
fs: fs,
}

// Test when the override file doesn't exist
t.Run("no existing file", func(t *testing.T) {
overrideContent := &ArgoOverrideFile{
Helm: struct {
Parameters []ArgoParameterOverride `yaml:"parameters"`
}{
Parameters: []ArgoParameterOverride{
{
Name: "param1",
Value: "value1",
},
},
},
}
result, err := repo.mergeOverrideFileContent("nonexistent.yaml", overrideContent)
assert.NoError(t, err)
assert.Equal(t, overrideContent, result)
})

// Test when the override file does exist
t.Run("existing file", func(t *testing.T) {
// Creating a dummy existing file
existingContent := ArgoOverrideFile{
Helm: struct {
Parameters []ArgoParameterOverride `yaml:"parameters"`
}{
Parameters: []ArgoParameterOverride{
{
Name: "param1",
Value: "oldValue1",
},
{
Name: "param2",
Value: "value2",
},
},
},
}

fileName := "existing.yaml"
contentBytes, _ := yaml.Marshal(existingContent)
file, _ := fs.Create(fileName)
_, err := file.Write(contentBytes)
assert.NoError(t, err)
err = file.Close()
assert.NoError(t, err)

// Merge with this content
overrideContent := &ArgoOverrideFile{
Helm: struct {
Parameters []ArgoParameterOverride `yaml:"parameters"`
}{
Parameters: []ArgoParameterOverride{
{
Name: "param1",
Value: "newValue1",
},
},
},
}

expectedMergedContent := &ArgoOverrideFile{
Helm: struct {
Parameters []ArgoParameterOverride `yaml:"parameters"`
}{
Parameters: []ArgoParameterOverride{
{
Name: "param1",
Value: "newValue1", // This assumes newValue1 overwrites oldValue1
},
{
Name: "param2",
Value: "value2",
},
},
},
}

result, err := repo.mergeOverrideFileContent(fileName, overrideContent)
assert.NoError(t, err)
assert.Equal(t, expectedMergedContent, result)
})

// Test when the existing override file has invalid YAML format
t.Run("invalid YAML file", func(t *testing.T) {
// Creating a dummy existing file with invalid YAML
invalidYAMLContent := `helm:
parameters:
- name: param1
value: value1` // The indentation is intentionally wrong to create an invalid YAML

fileName := "invalid.yaml"
file, _ := fs.Create(fileName)
_, err := file.Write([]byte(invalidYAMLContent))
assert.NoError(t, err)
err = file.Close()
assert.NoError(t, err)

overrideContent := &ArgoOverrideFile{
Helm: struct {
Parameters []ArgoParameterOverride `yaml:"parameters"`
}{
Parameters: []ArgoParameterOverride{
{
Name: "param1",
Value: "newValue1",
},
},
},
}

_, err = repo.mergeOverrideFileContent(fileName, overrideContent)
assert.Error(t, err)
})
}

func TestMergeParameters(t *testing.T) {
tests := []struct {
name string
Expand Down Expand Up @@ -199,3 +416,67 @@ func TestGitRepo_Close(t *testing.T) {
assert.Nil(t, repo.fs)
assert.Nil(t, repo.localRepo)
}

func TestVersionChanged(t *testing.T) {
t.Run("Repo without changes", func(t *testing.T) {
fs := memfs.New()
storer := memory.NewStorage()

repo, err := git.Init(storer, fs)
assert.NoError(t, err)

w, err := repo.Worktree()
assert.NoError(t, err)

changed, err := versionChanged(w)
assert.NoError(t, err)
assert.False(t, changed, "Expected no changes in a newly initialized repo")
})

t.Run("Repo with changes", func(t *testing.T) {
fs := memfs.New()
storer := memory.NewStorage()

repo, err := git.Init(storer, fs)
assert.NoError(t, err)

w, err := repo.Worktree()
assert.NoError(t, err)

// Create and commit a file
file, err := fs.Create("test.txt")
assert.NoError(t, err)
_, err = file.Write([]byte("Initial content"))
assert.NoError(t, err)
err = file.Close()
assert.NoError(t, err)

_, err = w.Add("test.txt")
assert.NoError(t, err)

_, err = w.Commit("Initial commit", &git.CommitOptions{
Author: &object.Signature{
Name: "John Doe",
Email: "johndoe@example.com",
When: time.Now(),
},
})
assert.NoError(t, err)

// Make changes to the file
file, err = fs.Create("test.txt")
assert.NoError(t, err)
_, err = file.Write([]byte("Updated content"))
assert.NoError(t, err)
err = file.Close()
assert.NoError(t, err)

_, err = w.Add("test.txt")
assert.NoError(t, err)

// Test versionChanged function
changed, err := versionChanged(w)
assert.NoError(t, err)
assert.True(t, changed, "Expected changes after modifying the file")
})
}

0 comments on commit 3785f83

Please sign in to comment.