Files
portainer/api/http/handler/gitops/sources/update_git_test.go
Phil Calder c60755fbc7 Update community branch (#13208)
Co-authored-by: Hannah Cooper <hannah.cooper@portainer.io>
Co-authored-by: Chaim Lev-Ari <chiptus@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Ali <83188384+testA113@users.noreply.github.com>
Co-authored-by: Steven Kang <skan070@gmail.com>
Co-authored-by: Josiah Clumont <josiah.clumont@portainer.io>
Co-authored-by: nickl-portainer <nicholas.loomans@portainer.io>
Co-authored-by: andres-portainer <91705312+andres-portainer@users.noreply.github.com>
Co-authored-by: Oscar Zhou <100548325+oscarzhou-portainer@users.noreply.github.com>
Co-authored-by: ferreiraborgesaxel-design <ferreiraborgesaxel-design@users.noreply.github.com>
2026-06-09 16:11:47 +12:00

322 lines
9.0 KiB
Go

package sources
import (
"net/http"
"net/http/httptest"
"testing"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/datastore"
gittypes "github.com/portainer/portainer/api/git/types"
"github.com/segmentio/encoding/json"
"github.com/stretchr/testify/require"
)
func TestGitSourceUpdate_Success(t *testing.T) {
t.Parallel()
_, store := datastore.MustNewTestStore(t, false, true)
var srcID portainer.SourceID
require.NoError(t, store.UpdateTx(func(tx dataservices.DataStoreTx) error {
src := &portainer.Source{Name: "old-name", Type: portainer.SourceTypeGit}
err := tx.Source().Create(src)
require.NoError(t, err)
srcID = src.ID
return tx.User().Create(&portainer.User{ID: 1, Role: portainer.AdministratorRole})
}))
h := newTestHandler(t, store)
body, err := json.Marshal(GitSourceUpdatePayload{
URL: new("https://github.com/org/new.git"),
Name: new("new-name"),
})
require.NoError(t, err)
rr := httptest.NewRecorder()
h.ServeHTTP(rr, buildUpdateReq(t, 1, int(srcID), body))
require.Equal(t, http.StatusOK, rr.Code)
var src portainer.Source
err = json.NewDecoder(rr.Body).Decode(&src)
require.NoError(t, err)
require.Equal(t, "new-name", src.Name)
require.NotNil(t, src.Git)
require.Equal(t, "https://github.com/org/new.git", src.Git.URL)
}
func TestGitSourceUpdate_PreservesAuthWhenNotProvided(t *testing.T) {
t.Parallel()
_, store := datastore.MustNewTestStore(t, false, true)
var srcID portainer.SourceID
require.NoError(t, store.UpdateTx(func(tx dataservices.DataStoreTx) error {
src := &portainer.Source{
Name: "auth-source",
Type: portainer.SourceTypeGit,
Git: &gittypes.RepoConfig{
URL: "https://github.com/org/repo.git",
Authentication: &gittypes.GitAuthentication{
Username: "alice",
Password: "secret",
},
},
}
err := tx.Source().Create(src)
require.NoError(t, err)
srcID = src.ID
return tx.User().Create(&portainer.User{ID: 1, Role: portainer.AdministratorRole})
}))
h := newTestHandler(t, store)
body, err := json.Marshal(GitSourceUpdatePayload{
URL: new("https://github.com/org/repo.git"),
Name: new("renamed"),
})
require.NoError(t, err)
rr := httptest.NewRecorder()
h.ServeHTTP(rr, buildUpdateReq(t, 1, int(srcID), body))
require.Equal(t, http.StatusOK, rr.Code)
var stored *portainer.Source
require.NoError(t, store.ViewTx(func(tx dataservices.DataStoreTx) error {
var err error
stored, err = tx.Source().Read(srcID)
return err
}))
require.NotNil(t, stored.Git)
require.NotNil(t, stored.Git.Authentication)
require.Equal(t, "alice", stored.Git.Authentication.Username)
require.Equal(t, "secret", stored.Git.Authentication.Password)
}
func TestGitSourceUpdate_ClearsAuthWhenRequested(t *testing.T) {
t.Parallel()
_, store := datastore.MustNewTestStore(t, false, true)
var srcID portainer.SourceID
require.NoError(t, store.UpdateTx(func(tx dataservices.DataStoreTx) error {
src := &portainer.Source{
Name: "auth-source",
Type: portainer.SourceTypeGit,
Git: &gittypes.RepoConfig{
URL: "https://github.com/org/repo.git",
Authentication: &gittypes.GitAuthentication{
Username: "alice",
Password: "secret",
},
},
}
err := tx.Source().Create(src)
require.NoError(t, err)
srcID = src.ID
return tx.User().Create(&portainer.User{ID: 1, Role: portainer.AdministratorRole})
}))
h := newTestHandler(t, store)
body, err := json.Marshal(GitSourceUpdatePayload{
URL: new("https://github.com/org/repo.git"),
Authentication: &GitAuthenticationUpdatePayload{},
})
require.NoError(t, err)
rr := httptest.NewRecorder()
h.ServeHTTP(rr, buildUpdateReq(t, 1, int(srcID), body))
require.Equal(t, http.StatusOK, rr.Code)
var stored *portainer.Source
require.NoError(t, store.ViewTx(func(tx dataservices.DataStoreTx) error {
var err error
stored, err = tx.Source().Read(srcID)
return err
}))
require.NotNil(t, stored.Git)
require.Nil(t, stored.Git.Authentication)
}
func TestGitSourceUpdate_ReplacesAuthWhenProvided(t *testing.T) {
t.Parallel()
_, store := datastore.MustNewTestStore(t, false, true)
var srcID portainer.SourceID
require.NoError(t, store.UpdateTx(func(tx dataservices.DataStoreTx) error {
src := &portainer.Source{
Name: "auth-source",
Type: portainer.SourceTypeGit,
Git: &gittypes.RepoConfig{
URL: "https://github.com/org/repo.git",
Authentication: &gittypes.GitAuthentication{
Username: "alice",
Password: "secret",
},
},
}
err := tx.Source().Create(src)
require.NoError(t, err)
srcID = src.ID
return tx.User().Create(&portainer.User{ID: 1, Role: portainer.AdministratorRole})
}))
h := newTestHandler(t, store)
body, err := json.Marshal(GitSourceUpdatePayload{
URL: new("https://github.com/org/repo.git"),
Authentication: &GitAuthenticationUpdatePayload{
Username: new("bob"),
Password: new("new-secret"),
},
})
require.NoError(t, err)
rr := httptest.NewRecorder()
h.ServeHTTP(rr, buildUpdateReq(t, 1, int(srcID), body))
require.Equal(t, http.StatusOK, rr.Code)
var stored *portainer.Source
require.NoError(t, store.ViewTx(func(tx dataservices.DataStoreTx) error {
var err error
stored, err = tx.Source().Read(srcID)
return err
}))
require.NotNil(t, stored.Git)
require.NotNil(t, stored.Git.Authentication)
require.Equal(t, "bob", stored.Git.Authentication.Username)
require.Equal(t, "new-secret", stored.Git.Authentication.Password)
}
func TestGitSourceUpdate_NotFound(t *testing.T) {
t.Parallel()
_, store := datastore.MustNewTestStore(t, false, true)
require.NoError(t, store.UpdateTx(func(tx dataservices.DataStoreTx) error {
return tx.User().Create(&portainer.User{ID: 1, Role: portainer.AdministratorRole})
}))
h := newTestHandler(t, store)
body, err := json.Marshal(GitSourceUpdatePayload{URL: new("https://github.com/org/repo.git")})
require.NoError(t, err)
rr := httptest.NewRecorder()
h.ServeHTTP(rr, buildUpdateReq(t, 1, 99, body))
require.Equal(t, http.StatusNotFound, rr.Code)
}
func TestGitSourceUpdate_ConflictOnDuplicateURL(t *testing.T) {
t.Parallel()
_, store := datastore.MustNewTestStore(t, false, true)
var srcID portainer.SourceID
require.NoError(t, store.UpdateTx(func(tx dataservices.DataStoreTx) error {
existing := &portainer.Source{
Name: "existing",
Type: portainer.SourceTypeGit,
Git: &gittypes.RepoConfig{
URL: "https://github.com/org/existing.git",
},
}
err := tx.Source().Create(existing)
require.NoError(t, err)
src := &portainer.Source{Name: "other", Type: portainer.SourceTypeGit}
err = tx.Source().Create(src)
require.NoError(t, err)
srcID = src.ID
return tx.User().Create(&portainer.User{ID: 1, Role: portainer.AdministratorRole})
}))
h := newTestHandler(t, store)
body, err := json.Marshal(GitSourceUpdatePayload{
URL: new("https://github.com/org/existing.git"),
})
require.NoError(t, err)
rr := httptest.NewRecorder()
h.ServeHTTP(rr, buildUpdateReq(t, 1, int(srcID), body))
require.Equal(t, http.StatusConflict, rr.Code)
}
func TestGitSourceUpdate_NotGitSource(t *testing.T) {
t.Parallel()
_, store := datastore.MustNewTestStore(t, false, true)
var srcID portainer.SourceID
require.NoError(t, store.UpdateTx(func(tx dataservices.DataStoreTx) error {
src := &portainer.Source{Name: "helm-source", Type: portainer.SourceTypeHelm}
err := tx.Source().Create(src)
require.NoError(t, err)
srcID = src.ID
return tx.User().Create(&portainer.User{ID: 1, Role: portainer.AdministratorRole})
}))
h := newTestHandler(t, store)
body, err := json.Marshal(GitSourceUpdatePayload{URL: new("https://github.com/org/repo.git")})
require.NoError(t, err)
rr := httptest.NewRecorder()
h.ServeHTTP(rr, buildUpdateReq(t, 1, int(srcID), body))
require.Equal(t, http.StatusBadRequest, rr.Code)
}
func TestGitSourceUpdate_MalformedJSON(t *testing.T) {
t.Parallel()
_, store := datastore.MustNewTestStore(t, false, true)
var srcID portainer.SourceID
require.NoError(t, store.UpdateTx(func(tx dataservices.DataStoreTx) error {
src := &portainer.Source{Name: "src", Type: portainer.SourceTypeGit}
err := tx.Source().Create(src)
require.NoError(t, err)
srcID = src.ID
return tx.User().Create(&portainer.User{ID: 1, Role: portainer.AdministratorRole})
}))
h := newTestHandler(t, store)
rr := httptest.NewRecorder()
h.ServeHTTP(rr, buildUpdateReq(t, 1, int(srcID), []byte("not-valid-json{")))
require.Equal(t, http.StatusBadRequest, rr.Code)
}
func TestGitSourceUpdate_NonNumericID(t *testing.T) {
t.Parallel()
_, store := datastore.MustNewTestStore(t, false, true)
require.NoError(t, store.UpdateTx(func(tx dataservices.DataStoreTx) error {
return tx.User().Create(&portainer.User{ID: 1, Role: portainer.AdministratorRole})
}))
h := newTestHandler(t, store)
body, err := json.Marshal(GitSourceUpdatePayload{URL: new("https://github.com/org/repo.git")})
require.NoError(t, err)
req := buildUpdateReqWithRawID(t, 1, "not-a-number", body)
rr := httptest.NewRecorder()
h.ServeHTTP(rr, req)
require.Equal(t, http.StatusBadRequest, rr.Code)
}