ca5f695459
Co-authored-by: andres-portainer <91705312+andres-portainer@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
171 lines
5.3 KiB
Go
171 lines
5.3 KiB
Go
package sources
|
|
|
|
import (
|
|
"testing"
|
|
|
|
portainer "github.com/portainer/portainer/api"
|
|
gittypes "github.com/portainer/portainer/api/git/types"
|
|
ce "github.com/portainer/portainer/api/gitops/workflows"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestWorkflowsBySourceID(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("groups workflows with same URL and credentials", func(t *testing.T) {
|
|
t.Parallel()
|
|
cfg := gitCfg("https://github.com/org/repo")
|
|
wfs := []ce.Workflow{{GitConfig: cfg}, {GitConfig: cfg}}
|
|
byID := workflowsBySourceID(wfs)
|
|
assert.Len(t, byID, 1)
|
|
for _, group := range byID {
|
|
assert.Len(t, group, 2)
|
|
}
|
|
})
|
|
|
|
t.Run("separates workflows with same URL but different credentials", func(t *testing.T) {
|
|
t.Parallel()
|
|
wfs := []ce.Workflow{
|
|
{GitConfig: &gittypes.RepoConfig{URL: "https://github.com/org/repo",
|
|
Authentication: &gittypes.GitAuthentication{Username: "alice", Password: "pass1"}}},
|
|
{GitConfig: &gittypes.RepoConfig{URL: "https://github.com/org/repo",
|
|
Authentication: &gittypes.GitAuthentication{Username: "bob", Password: "pass2"}}},
|
|
}
|
|
byID := workflowsBySourceID(wfs)
|
|
assert.Len(t, byID, 2)
|
|
})
|
|
|
|
t.Run("skips workflows with nil GitConfig", func(t *testing.T) {
|
|
t.Parallel()
|
|
wfs := []ce.Workflow{{}, {GitConfig: gitCfg("https://github.com/org/repo")}}
|
|
byID := workflowsBySourceID(wfs)
|
|
assert.Len(t, byID, 1)
|
|
})
|
|
}
|
|
|
|
func TestBuildSource(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("status is the worst across all workflows", func(t *testing.T) {
|
|
t.Parallel()
|
|
wfs := []ce.Workflow{
|
|
{Status: ce.WorkflowStatusObject{Source: ce.WorkflowPhaseStatus{Status: ce.StatusHealthy}}},
|
|
{Status: ce.WorkflowStatusObject{Source: ce.WorkflowPhaseStatus{Status: ce.StatusError, Error: "boom"}}},
|
|
}
|
|
s := buildSource("id", "https://github.com/org/repo.git", wfs)
|
|
assert.Equal(t, ce.StatusError, s.Status)
|
|
assert.Equal(t, "boom", s.Error)
|
|
})
|
|
|
|
t.Run("usedBy equals the number of workflows", func(t *testing.T) {
|
|
t.Parallel()
|
|
wfs := make([]ce.Workflow, 3)
|
|
s := buildSource("id", "https://github.com/org/repo", wfs)
|
|
assert.Equal(t, 3, s.UsedBy)
|
|
})
|
|
|
|
t.Run("environments deduplicates endpoint IDs", func(t *testing.T) {
|
|
t.Parallel()
|
|
wfs := []ce.Workflow{
|
|
{Target: ce.Target{EndpointID: portainer.EndpointID(1)}},
|
|
{Target: ce.Target{EndpointID: portainer.EndpointID(1)}}, // duplicate
|
|
{Target: ce.Target{EndpointID: portainer.EndpointID(2)}},
|
|
}
|
|
s := buildSource("id", "https://github.com/org/repo", wfs)
|
|
assert.Equal(t, 2, s.Environments)
|
|
})
|
|
|
|
t.Run("name is extracted from URL", func(t *testing.T) {
|
|
t.Parallel()
|
|
s := buildSource("id", "https://github.com/org/my-app.git", []ce.Workflow{{}})
|
|
assert.Equal(t, "my-app", s.Name)
|
|
})
|
|
|
|
t.Run("lastSync is the maximum across all workflows", func(t *testing.T) {
|
|
t.Parallel()
|
|
wfs := []ce.Workflow{
|
|
{LastSyncDate: 100},
|
|
{LastSyncDate: 300},
|
|
{LastSyncDate: 200},
|
|
}
|
|
s := buildSource("id", "https://github.com/org/repo", wfs)
|
|
assert.Equal(t, int64(300), s.LastSync)
|
|
})
|
|
}
|
|
|
|
func TestRedactWorkflowCredentials(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("clears password and preserves username", func(t *testing.T) {
|
|
t.Parallel()
|
|
wfs := []ce.Workflow{{GitConfig: &gittypes.RepoConfig{
|
|
Authentication: &gittypes.GitAuthentication{Username: "user", Password: "s3cr3t"},
|
|
}}}
|
|
got := redactWorkflowCredentials(wfs)
|
|
require.NotNil(t, got[0].GitConfig.Authentication)
|
|
assert.Equal(t, "user", got[0].GitConfig.Authentication.Username)
|
|
assert.Empty(t, got[0].GitConfig.Authentication.Password)
|
|
})
|
|
|
|
t.Run("does not mutate the original slice", func(t *testing.T) {
|
|
t.Parallel()
|
|
wfs := []ce.Workflow{{GitConfig: &gittypes.RepoConfig{
|
|
Authentication: &gittypes.GitAuthentication{Password: "s3cr3t"},
|
|
}}}
|
|
_ = redactWorkflowCredentials(wfs)
|
|
assert.Equal(t, "s3cr3t", wfs[0].GitConfig.Authentication.Password)
|
|
})
|
|
|
|
t.Run("nil GitConfig is safe", func(t *testing.T) {
|
|
t.Parallel()
|
|
assert.NotPanics(t, func() { redactWorkflowCredentials([]ce.Workflow{{}}) })
|
|
})
|
|
|
|
t.Run("nil Authentication is safe", func(t *testing.T) {
|
|
t.Parallel()
|
|
wfs := []ce.Workflow{{GitConfig: &gittypes.RepoConfig{}}}
|
|
assert.NotPanics(t, func() { redactWorkflowCredentials(wfs) })
|
|
})
|
|
}
|
|
|
|
func TestBuildAutoUpdateInfo(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
assert.Nil(t, buildAutoUpdateInfo(nil))
|
|
assert.Nil(t, buildAutoUpdateInfo(&portainer.AutoUpdateSettings{}))
|
|
|
|
got := buildAutoUpdateInfo(&portainer.AutoUpdateSettings{Interval: "5m"})
|
|
require.NotNil(t, got)
|
|
assert.Equal(t, "Interval", got.Mechanism)
|
|
assert.Equal(t, "5m", got.FetchInterval)
|
|
|
|
got = buildAutoUpdateInfo(&portainer.AutoUpdateSettings{Webhook: "abc123"})
|
|
require.NotNil(t, got)
|
|
assert.Equal(t, "Webhook", got.Mechanism)
|
|
assert.Empty(t, got.FetchInterval)
|
|
}
|
|
|
|
func TestBuildConnectionInfo(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
assert.Nil(t, buildConnectionInfo(nil))
|
|
|
|
cfg := &gittypes.RepoConfig{
|
|
ReferenceName: "refs/heads/main",
|
|
ConfigFilePath: "docker-compose.yml",
|
|
TLSSkipVerify: true,
|
|
Authentication: &gittypes.GitAuthentication{Username: "user"},
|
|
}
|
|
got := buildConnectionInfo(cfg)
|
|
require.NotNil(t, got)
|
|
assert.Equal(t, "refs/heads/main", got.ReferenceName)
|
|
assert.Equal(t, "docker-compose.yml", got.ConfigFilePath)
|
|
assert.True(t, got.TLSSkipVerify)
|
|
assert.True(t, got.Authentication)
|
|
|
|
got = buildConnectionInfo(&gittypes.RepoConfig{})
|
|
assert.False(t, got.Authentication)
|
|
}
|