chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,662 @@
|
||||
package git
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/go-git/go-git/v5"
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
githttp "github.com/go-git/go-git/v5/plumbing/transport/http"
|
||||
gittypes "github.com/portainer/portainer/api/git/types"
|
||||
"github.com/portainer/portainer/pkg/fips"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_buildDownloadUrl(t *testing.T) {
|
||||
t.Parallel()
|
||||
a := NewAzureClient()
|
||||
u, err := a.buildDownloadUrl(&azureOptions{
|
||||
organisation: "organisation",
|
||||
project: "project",
|
||||
repository: "repository",
|
||||
}, "refs/heads/main")
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedUrl, err := url.Parse("https://dev.azure.com/organisation/project/_apis/git/repositories/repository/items?scopePath=/&download=true&versionDescriptor.version=main&$format=zip&recursionLevel=full&api-version=6.0&versionDescriptor.versionType=branch")
|
||||
require.NoError(t, err)
|
||||
|
||||
actualUrl, err := url.Parse(u)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, expectedUrl.Host, actualUrl.Host)
|
||||
assert.Equal(t, expectedUrl.Scheme, actualUrl.Scheme)
|
||||
assert.Equal(t, expectedUrl.Path, actualUrl.Path)
|
||||
assert.Equal(t, expectedUrl.Query(), actualUrl.Query())
|
||||
}
|
||||
|
||||
func Test_buildRootItemUrl(t *testing.T) {
|
||||
t.Parallel()
|
||||
a := NewAzureClient()
|
||||
u, err := a.buildRootItemUrl(&azureOptions{
|
||||
organisation: "organisation",
|
||||
project: "project",
|
||||
repository: "repository",
|
||||
}, "refs/heads/main")
|
||||
|
||||
expectedUrl, _ := url.Parse("https://dev.azure.com/organisation/project/_apis/git/repositories/repository/items?scopePath=/&api-version=6.0&versionDescriptor.version=main&versionDescriptor.versionType=branch")
|
||||
actualUrl, _ := url.Parse(u)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, expectedUrl.Host, actualUrl.Host)
|
||||
assert.Equal(t, expectedUrl.Scheme, actualUrl.Scheme)
|
||||
assert.Equal(t, expectedUrl.Path, actualUrl.Path)
|
||||
assert.Equal(t, expectedUrl.Query(), actualUrl.Query())
|
||||
}
|
||||
|
||||
func Test_buildRefsUrl(t *testing.T) {
|
||||
t.Parallel()
|
||||
a := NewAzureClient()
|
||||
u, err := a.buildRefsUrl(&azureOptions{
|
||||
organisation: "organisation",
|
||||
project: "project",
|
||||
repository: "repository",
|
||||
})
|
||||
|
||||
expectedUrl, _ := url.Parse("https://dev.azure.com/organisation/project/_apis/git/repositories/repository/refs?api-version=6.0")
|
||||
actualUrl, _ := url.Parse(u)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, expectedUrl.Host, actualUrl.Host)
|
||||
assert.Equal(t, expectedUrl.Scheme, actualUrl.Scheme)
|
||||
assert.Equal(t, expectedUrl.Path, actualUrl.Path)
|
||||
assert.Equal(t, expectedUrl.Query(), actualUrl.Query())
|
||||
}
|
||||
|
||||
func Test_buildTreeUrl(t *testing.T) {
|
||||
t.Parallel()
|
||||
a := NewAzureClient()
|
||||
u, err := a.buildTreeUrl(&azureOptions{
|
||||
organisation: "organisation",
|
||||
project: "project",
|
||||
repository: "repository",
|
||||
}, "sha1")
|
||||
|
||||
expectedUrl, _ := url.Parse("https://dev.azure.com/organisation/project/_apis/git/repositories/repository/trees/sha1?api-version=6.0&recursive=true")
|
||||
actualUrl, _ := url.Parse(u)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, expectedUrl.Host, actualUrl.Host)
|
||||
assert.Equal(t, expectedUrl.Scheme, actualUrl.Scheme)
|
||||
assert.Equal(t, expectedUrl.Path, actualUrl.Path)
|
||||
assert.Equal(t, expectedUrl.Query(), actualUrl.Query())
|
||||
}
|
||||
|
||||
func Test_parseAzureUrl(t *testing.T) {
|
||||
t.Parallel()
|
||||
type args struct {
|
||||
url string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *azureOptions
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Expected SSH URL format starting with ssh://",
|
||||
args: args{
|
||||
url: "ssh://git@ssh.dev.azure.com:v3/Organisation/Project/Repository",
|
||||
},
|
||||
want: &azureOptions{
|
||||
organisation: "Organisation",
|
||||
project: "Project",
|
||||
repository: "Repository",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Expected SSH URL format starting with git@ssh",
|
||||
args: args{
|
||||
url: "git@ssh.dev.azure.com:v3/Organisation/Project/Repository",
|
||||
},
|
||||
want: &azureOptions{
|
||||
organisation: "Organisation",
|
||||
project: "Project",
|
||||
repository: "Repository",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Unexpected SSH URL format",
|
||||
args: args{
|
||||
url: "git@ssh.dev.azure.com:v3/Organisation/Repository",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Expected HTTPS URL format",
|
||||
args: args{
|
||||
url: "https://Organisation@dev.azure.com/Organisation/Project/_git/Repository",
|
||||
},
|
||||
want: &azureOptions{
|
||||
organisation: "Organisation",
|
||||
project: "Project",
|
||||
repository: "Repository",
|
||||
username: "Organisation",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "HTTPS URL with credentials",
|
||||
args: args{
|
||||
url: "https://username:password@dev.azure.com/Organisation/Project/_git/Repository",
|
||||
},
|
||||
want: &azureOptions{
|
||||
organisation: "Organisation",
|
||||
project: "Project",
|
||||
repository: "Repository",
|
||||
username: "username",
|
||||
password: "password",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "HTTPS URL with password",
|
||||
args: args{
|
||||
url: "https://:password@dev.azure.com/Organisation/Project/_git/Repository",
|
||||
},
|
||||
want: &azureOptions{
|
||||
organisation: "Organisation",
|
||||
project: "Project",
|
||||
repository: "Repository",
|
||||
password: "password",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Visual Studio HTTPS URL with credentials",
|
||||
args: args{
|
||||
url: "https://username:password@organisation.visualstudio.com/project/_git/repository",
|
||||
},
|
||||
want: &azureOptions{
|
||||
organisation: "organisation",
|
||||
project: "project",
|
||||
repository: "repository",
|
||||
username: "username",
|
||||
password: "password",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Unexpected HTTPS URL format",
|
||||
args: args{
|
||||
url: "https://Organisation@dev.azure.com/Project/_git/Repository",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := parseUrl(tt.args.url)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("parseUrl() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_isAzureUrl(t *testing.T) {
|
||||
t.Parallel()
|
||||
type args struct {
|
||||
s string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "Is Azure url",
|
||||
args: args{
|
||||
s: "https://Organisation@dev.azure.com/Organisation/Project/_git/Repository",
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "Is Azure url",
|
||||
args: args{
|
||||
s: "https://portainer.visualstudio.com/project/_git/repository",
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "Is NOT Azure url",
|
||||
args: args{
|
||||
s: "https://github.com/Organisation/Repository",
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equal(t, tt.want, IsAzureUrl(tt.args.s))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_azureDownloader_downloadZipFromAzureDevOps(t *testing.T) {
|
||||
t.Parallel()
|
||||
fips.InitFIPS(false)
|
||||
|
||||
type args struct {
|
||||
repositoryUrl string
|
||||
username string
|
||||
password string
|
||||
}
|
||||
type basicAuth struct {
|
||||
username, password string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *basicAuth
|
||||
}{
|
||||
{
|
||||
name: "username, password embedded",
|
||||
args: args{
|
||||
repositoryUrl: "https://username:password@dev.azure.com/Organisation/Project/_git/Repository",
|
||||
},
|
||||
want: &basicAuth{
|
||||
username: "username",
|
||||
password: "password",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "username, password embedded, clone options take precedence",
|
||||
args: args{
|
||||
repositoryUrl: "https://username:password@dev.azure.com/Organisation/Project/_git/Repository",
|
||||
username: "u",
|
||||
password: "p",
|
||||
},
|
||||
want: &basicAuth{
|
||||
username: "u",
|
||||
password: "p",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no credentials",
|
||||
args: args{
|
||||
repositoryUrl: "https://dev.azure.com/Organisation/Project/_git/Repository",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var zipRequestAuth *basicAuth
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if username, password, ok := r.BasicAuth(); ok {
|
||||
zipRequestAuth = &basicAuth{username, password}
|
||||
}
|
||||
w.WriteHeader(http.StatusNotFound) // this makes function under test to return an error
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
a := &azureClient{
|
||||
baseUrl: server.URL,
|
||||
}
|
||||
|
||||
option := &git.CloneOptions{
|
||||
URL: tt.args.repositoryUrl,
|
||||
}
|
||||
if tt.args.username != "" || tt.args.password != "" {
|
||||
option.Auth = &githttp.BasicAuth{
|
||||
Username: tt.args.username,
|
||||
Password: tt.args.password,
|
||||
}
|
||||
}
|
||||
_, err := a.downloadZipFromAzureDevOps(t.Context(), option)
|
||||
require.Error(t, err)
|
||||
assert.Equal(t, tt.want, zipRequestAuth)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_azureDownloader_latestCommitID(t *testing.T) {
|
||||
t.Parallel()
|
||||
fips.InitFIPS(false)
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
response := `{
|
||||
"count": 1,
|
||||
"value": [
|
||||
{
|
||||
"objectId": "1a5630f017127db7de24d8771da0f536ff98fc9b",
|
||||
"gitObjectType": "tree",
|
||||
"commitId": "27104ad7549d9e66685e115a497533f18024be9c",
|
||||
"path": "/",
|
||||
"isFolder": true,
|
||||
"url": "https://dev.azure.com/simonmeng0474/4b546a97-c481-4506-bdd5-976e9592f91a/_apis/git/repositories/a22247ad-053f-43bc-88a7-62ff4846bb97/items?path=%2F&versionType=Branch&versionOptions=None"
|
||||
}
|
||||
]
|
||||
}`
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
_, _ = w.Write([]byte(response))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
a := &azureClient{baseUrl: server.URL}
|
||||
|
||||
type args struct {
|
||||
repositoryUrl string
|
||||
referenceName string
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "should be able to parse response",
|
||||
args: args{
|
||||
repositoryUrl: "https://dev.azure.com/Organisation/Project/_git/Repository",
|
||||
referenceName: "",
|
||||
},
|
||||
want: "27104ad7549d9e66685e115a497533f18024be9c",
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
id, err := a.LatestCommitID(t.Context(), tt.args.repositoryUrl, tt.args.referenceName, &git.ListOptions{})
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("azureDownloader.latestCommitID() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
assert.Equal(t, tt.want, id)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type testRepoManager struct {
|
||||
called bool
|
||||
}
|
||||
|
||||
func (t *testRepoManager) Download(_ context.Context, _ string, _ *git.CloneOptions) error {
|
||||
t.called = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *testRepoManager) LatestCommitID(_ context.Context, _, _ string, _ *git.ListOptions) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (t *testRepoManager) ListRefs(_ context.Context, _ string, _ *git.ListOptions) ([]string, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (t *testRepoManager) ListFiles(_ context.Context, _ bool, _ *git.CloneOptions) ([]string, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func Test_cloneRepository_azure(t *testing.T) {
|
||||
t.Parallel()
|
||||
tests := []struct {
|
||||
name string
|
||||
url string
|
||||
called bool
|
||||
}{
|
||||
{
|
||||
name: "Azure HTTP URL",
|
||||
url: "https://Organisation@dev.azure.com/Organisation/Project/_git/Repository",
|
||||
called: true,
|
||||
},
|
||||
{
|
||||
name: "Azure SSH URL",
|
||||
url: "git@ssh.dev.azure.com:v3/Organisation/Project/Repository",
|
||||
called: true,
|
||||
},
|
||||
{
|
||||
name: "Something else",
|
||||
url: "https://example.com",
|
||||
called: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
azure := &testRepoManager{}
|
||||
git := &testRepoManager{}
|
||||
|
||||
s := &Service{azure: azure, git: git}
|
||||
err := s.CloneRepository(t.Context(), "", tt.url, "", "", "", false)
|
||||
require.NoError(t, err)
|
||||
|
||||
// if azure API is called, git isn't and vice versa
|
||||
assert.Equal(t, tt.called, azure.called)
|
||||
assert.Equal(t, tt.called, !git.called)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_listRefs_azure(t *testing.T) {
|
||||
t.Parallel()
|
||||
ensureIntegrationTest(t)
|
||||
|
||||
client := NewAzureClient()
|
||||
|
||||
type args struct {
|
||||
repositoryUrl string
|
||||
username string
|
||||
password string
|
||||
}
|
||||
|
||||
type expectResult struct {
|
||||
err error
|
||||
refsCount int
|
||||
}
|
||||
|
||||
accessToken := getRequiredValue(t, "AZURE_DEVOPS_PAT")
|
||||
username := getRequiredValue(t, "AZURE_DEVOPS_USERNAME")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
expect expectResult
|
||||
}{
|
||||
{
|
||||
name: "list refs of a real repository",
|
||||
args: args{
|
||||
repositoryUrl: privateAzureRepoURL,
|
||||
username: username,
|
||||
password: accessToken,
|
||||
},
|
||||
expect: expectResult{
|
||||
err: nil,
|
||||
refsCount: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list refs of a real repository with incorrect credential",
|
||||
args: args{
|
||||
repositoryUrl: privateAzureRepoURL,
|
||||
username: "test-username",
|
||||
password: "test-token",
|
||||
},
|
||||
expect: expectResult{
|
||||
err: gittypes.ErrAuthenticationFailure,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list refs of a real repository without providing credential",
|
||||
args: args{
|
||||
repositoryUrl: privateAzureRepoURL,
|
||||
username: "",
|
||||
password: "",
|
||||
},
|
||||
expect: expectResult{
|
||||
err: gittypes.ErrAuthenticationFailure,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list refs of a fake repository",
|
||||
args: args{
|
||||
repositoryUrl: privateAzureRepoURL + "fake",
|
||||
username: username,
|
||||
password: accessToken,
|
||||
},
|
||||
expect: expectResult{
|
||||
err: gittypes.ErrIncorrectRepositoryURL,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
option := &git.ListOptions{}
|
||||
if tt.args.username != "" || tt.args.password != "" {
|
||||
option.Auth = &githttp.BasicAuth{
|
||||
Username: tt.args.username,
|
||||
Password: tt.args.password,
|
||||
}
|
||||
}
|
||||
refs, err := client.ListRefs(t.Context(), tt.args.repositoryUrl, option)
|
||||
if tt.expect.err == nil {
|
||||
require.NoError(t, err)
|
||||
if tt.expect.refsCount > 0 {
|
||||
assert.NotEmpty(t, refs)
|
||||
}
|
||||
} else {
|
||||
require.Error(t, err)
|
||||
assert.Equal(t, tt.expect.err, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_listFiles_azure(t *testing.T) {
|
||||
t.Parallel()
|
||||
ensureIntegrationTest(t)
|
||||
|
||||
client := NewAzureClient()
|
||||
|
||||
type args struct {
|
||||
repositoryUrl string
|
||||
referenceName string
|
||||
username string
|
||||
password string
|
||||
}
|
||||
|
||||
type expectResult struct {
|
||||
shouldFail bool
|
||||
err error
|
||||
matchedCount int
|
||||
}
|
||||
|
||||
accessToken := getRequiredValue(t, "AZURE_DEVOPS_PAT")
|
||||
username := getRequiredValue(t, "AZURE_DEVOPS_USERNAME")
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
expect expectResult
|
||||
}{
|
||||
{
|
||||
name: "list tree with real repository and head ref but incorrect credential",
|
||||
args: args{
|
||||
repositoryUrl: privateAzureRepoURL,
|
||||
referenceName: "refs/heads/main",
|
||||
username: "test-username",
|
||||
password: "test-token",
|
||||
},
|
||||
expect: expectResult{
|
||||
shouldFail: true,
|
||||
err: gittypes.ErrAuthenticationFailure,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list tree with real repository and head ref but no credential",
|
||||
args: args{
|
||||
repositoryUrl: privateAzureRepoURL,
|
||||
referenceName: "refs/heads/main",
|
||||
username: "",
|
||||
password: "",
|
||||
},
|
||||
expect: expectResult{
|
||||
shouldFail: true,
|
||||
err: gittypes.ErrAuthenticationFailure,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list tree with real repository and head ref",
|
||||
args: args{
|
||||
repositoryUrl: privateAzureRepoURL,
|
||||
referenceName: "refs/heads/main",
|
||||
username: username,
|
||||
password: accessToken,
|
||||
},
|
||||
expect: expectResult{
|
||||
err: nil,
|
||||
matchedCount: 19,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list tree with real repository but non-existing ref",
|
||||
args: args{
|
||||
repositoryUrl: privateAzureRepoURL,
|
||||
referenceName: "refs/fake/feature",
|
||||
username: username,
|
||||
password: accessToken,
|
||||
},
|
||||
expect: expectResult{
|
||||
shouldFail: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list tree with fake repository ",
|
||||
args: args{
|
||||
repositoryUrl: privateAzureRepoURL + "fake",
|
||||
referenceName: "refs/fake/feature",
|
||||
username: username,
|
||||
password: accessToken,
|
||||
},
|
||||
expect: expectResult{
|
||||
shouldFail: true,
|
||||
err: gittypes.ErrIncorrectRepositoryURL,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
option := &git.CloneOptions{
|
||||
URL: tt.args.repositoryUrl,
|
||||
ReferenceName: plumbing.ReferenceName(tt.args.referenceName),
|
||||
}
|
||||
if tt.args.username != "" || tt.args.password != "" {
|
||||
option.Auth = &githttp.BasicAuth{
|
||||
Username: tt.args.username,
|
||||
Password: tt.args.password,
|
||||
}
|
||||
}
|
||||
paths, err := client.ListFiles(t.Context(), false, option)
|
||||
if tt.expect.shouldFail {
|
||||
require.Error(t, err)
|
||||
if tt.expect.err != nil {
|
||||
assert.Equal(t, tt.expect.err, err)
|
||||
}
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
if tt.expect.matchedCount > 0 {
|
||||
assert.NotEmpty(t, paths)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user