283 lines
8.7 KiB
Go
283 lines
8.7 KiB
Go
package workflows
|
|
|
|
import (
|
|
"strconv"
|
|
"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/portainer/portainer/api/http/security"
|
|
"github.com/portainer/portainer/api/set"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func adminContext() *security.RestrictedRequestContext {
|
|
return &security.RestrictedRequestContext{IsAdmin: true, UserID: 1}
|
|
}
|
|
|
|
func mustCreateGitWorkflow(t *testing.T, tx dataservices.DataStoreTx, stack *portainer.Stack) {
|
|
t.Helper()
|
|
|
|
cfg := stack.GitConfig
|
|
|
|
src := &portainer.Source{Type: portainer.SourceTypeGit, Git: cfg}
|
|
require.NoError(t, tx.Source().Create(src))
|
|
|
|
wf := &portainer.Workflow{Artifacts: []portainer.Artifact{{
|
|
StackID: stack.ID,
|
|
Files: []portainer.ArtifactFile{{SourceID: src.ID}},
|
|
}}}
|
|
require.NoError(t, tx.Workflow().Create(wf))
|
|
|
|
stack.WorkflowID = wf.ID
|
|
stack.GitConfig = nil
|
|
|
|
require.NoError(t, tx.Stack().Create(stack))
|
|
}
|
|
|
|
func TestAddSourceStats_NoOp(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
result := make(map[portainer.SourceID]SourceStats)
|
|
addSourceStats(result, nil, nil, 0)
|
|
|
|
require.Empty(t, result)
|
|
}
|
|
|
|
func TestAddSourceStats_AccumulatesWorkflowCount(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
result := make(map[portainer.SourceID]SourceStats)
|
|
addSourceStats(result, []portainer.SourceID{1}, nil, 0)
|
|
addSourceStats(result, []portainer.SourceID{1}, nil, 0)
|
|
|
|
require.Equal(t, 2, result[1].WorkflowCount)
|
|
}
|
|
|
|
func TestAddSourceStats_CollectsUniqueEndpointIDs(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
result := make(map[portainer.SourceID]SourceStats)
|
|
addSourceStats(result, []portainer.SourceID{1}, []portainer.EndpointID{10, 20}, 0)
|
|
addSourceStats(result, []portainer.SourceID{1}, []portainer.EndpointID{20, 30}, 0)
|
|
|
|
require.Len(t, result[1].EndpointIDs, 3)
|
|
require.True(t, result[1].EndpointIDs[10])
|
|
require.True(t, result[1].EndpointIDs[20])
|
|
require.True(t, result[1].EndpointIDs[30])
|
|
}
|
|
|
|
func TestAddSourceStats_MaxLastSync(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
result := make(map[portainer.SourceID]SourceStats)
|
|
addSourceStats(result, []portainer.SourceID{1}, nil, 100)
|
|
addSourceStats(result, []portainer.SourceID{1}, nil, 500)
|
|
addSourceStats(result, []portainer.SourceID{1}, nil, 200)
|
|
|
|
require.Equal(t, int64(500), result[1].LastSync)
|
|
}
|
|
|
|
func TestAddSourceStats_MultipleSourceIDs(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
result := make(map[portainer.SourceID]SourceStats)
|
|
addSourceStats(result, []portainer.SourceID{1, 2}, []portainer.EndpointID{10}, 100)
|
|
|
|
require.Equal(t, 1, result[1].WorkflowCount)
|
|
require.Equal(t, 1, result[2].WorkflowCount)
|
|
require.True(t, result[1].EndpointIDs[10])
|
|
require.True(t, result[2].EndpointIDs[10])
|
|
}
|
|
|
|
func TestFetchWorkflows_ReturnsOnlyGitopsStacks(t *testing.T) {
|
|
t.Parallel()
|
|
_, store := datastore.MustNewTestStore(t, false, true)
|
|
|
|
require.NoError(t, store.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
|
mustCreateGitWorkflow(t, tx, &portainer.Stack{
|
|
ID: 1,
|
|
Name: "gitops-stack",
|
|
GitConfig: &gittypes.RepoConfig{URL: "https://github.com/x/repo"},
|
|
})
|
|
require.NoError(t, tx.Stack().Create(&portainer.Stack{ID: 2, Name: "plain-stack"}))
|
|
|
|
return tx.User().Create(&portainer.User{ID: 1, Role: portainer.AdministratorRole})
|
|
}))
|
|
|
|
var items []Workflow
|
|
require.NoError(t, store.ViewTx(func(tx dataservices.DataStoreTx) error {
|
|
var err error
|
|
items, err = FetchWorkflows(t.Context(), tx, nil, nil, adminContext(), nil)
|
|
return err
|
|
}))
|
|
require.Len(t, items, 1)
|
|
require.Equal(t, "gitops-stack", items[0].Name)
|
|
}
|
|
|
|
func TestFetchWorkflows_FiltersByEndpointID(t *testing.T) {
|
|
t.Parallel()
|
|
_, store := datastore.MustNewTestStore(t, false, true)
|
|
|
|
require.NoError(t, store.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
|
for i := 1; i <= 3; i++ {
|
|
mustCreateGitWorkflow(t, tx, &portainer.Stack{
|
|
ID: portainer.StackID(i),
|
|
Name: "stack-" + strconv.Itoa(i),
|
|
EndpointID: portainer.EndpointID(i),
|
|
GitConfig: &gittypes.RepoConfig{URL: "https://github.com/x/" + strconv.Itoa(i)},
|
|
})
|
|
}
|
|
|
|
return tx.User().Create(&portainer.User{ID: 1, Role: portainer.AdministratorRole})
|
|
}))
|
|
|
|
var items []Workflow
|
|
require.NoError(t, store.ViewTx(func(tx dataservices.DataStoreTx) error {
|
|
var err error
|
|
items, err = FetchWorkflows(t.Context(), tx, nil, nil, adminContext(), set.ToSet([]portainer.EndpointID{1, 2}))
|
|
return err
|
|
}))
|
|
require.Len(t, items, 2)
|
|
|
|
names := []string{items[0].Name, items[1].Name}
|
|
require.Contains(t, names, "stack-1")
|
|
require.Contains(t, names, "stack-2")
|
|
}
|
|
|
|
func TestFetchWorkflows_EmptyWhenNoGitopsStacks(t *testing.T) {
|
|
t.Parallel()
|
|
_, store := datastore.MustNewTestStore(t, false, true)
|
|
|
|
require.NoError(t, store.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
|
require.NoError(t, tx.Stack().Create(&portainer.Stack{ID: 1, Name: "plain-1"}))
|
|
require.NoError(t, tx.Stack().Create(&portainer.Stack{ID: 2, Name: "plain-2"}))
|
|
|
|
return tx.User().Create(&portainer.User{ID: 1, Role: portainer.AdministratorRole})
|
|
}))
|
|
|
|
var items []Workflow
|
|
require.NoError(t, store.ViewTx(func(tx dataservices.DataStoreTx) error {
|
|
var err error
|
|
items, err = FetchWorkflows(t.Context(), tx, nil, nil, adminContext(), nil)
|
|
return err
|
|
}))
|
|
require.Empty(t, items)
|
|
}
|
|
|
|
func TestFetchWorkflows_NilEndpointSetReturnsAll(t *testing.T) {
|
|
t.Parallel()
|
|
_, store := datastore.MustNewTestStore(t, false, true)
|
|
|
|
require.NoError(t, store.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
|
for i := 1; i <= 3; i++ {
|
|
mustCreateGitWorkflow(t, tx, &portainer.Stack{
|
|
ID: portainer.StackID(i),
|
|
Name: "stack-" + strconv.Itoa(i),
|
|
EndpointID: portainer.EndpointID(i),
|
|
GitConfig: &gittypes.RepoConfig{URL: "https://github.com/x/" + strconv.Itoa(i)},
|
|
})
|
|
}
|
|
|
|
return tx.User().Create(&portainer.User{ID: 1, Role: portainer.AdministratorRole})
|
|
}))
|
|
|
|
var items []Workflow
|
|
require.NoError(t, store.ViewTx(func(tx dataservices.DataStoreTx) error {
|
|
var err error
|
|
items, err = FetchWorkflows(t.Context(), tx, nil, nil, adminContext(), nil)
|
|
return err
|
|
}))
|
|
require.Len(t, items, 3)
|
|
}
|
|
|
|
func TestFetchSourceStats_ReturnsAllSources(t *testing.T) {
|
|
t.Parallel()
|
|
_, store := datastore.MustNewTestStore(t, false, true)
|
|
|
|
require.NoError(t, store.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
|
require.NoError(t, tx.Source().Create(&portainer.Source{Name: "source-1", Type: portainer.SourceTypeGit}))
|
|
require.NoError(t, tx.Source().Create(&portainer.Source{Name: "source-2", Type: portainer.SourceTypeGit}))
|
|
|
|
return tx.User().Create(&portainer.User{ID: 1, Role: portainer.AdministratorRole})
|
|
}))
|
|
|
|
var sources []portainer.Source
|
|
require.NoError(t, store.ViewTx(func(tx dataservices.DataStoreTx) error {
|
|
var err error
|
|
sources, _, err = FetchSourceStats(tx, nil, adminContext())
|
|
|
|
return err
|
|
}))
|
|
|
|
require.Len(t, sources, 2)
|
|
}
|
|
|
|
func TestFetchSourceStats_TracksWorkflowCountAndEndpoints(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: "shared", Type: portainer.SourceTypeGit}
|
|
require.NoError(t, tx.Source().Create(src))
|
|
srcID = src.ID
|
|
|
|
for i := 1; i <= 2; i++ {
|
|
wf := &portainer.Workflow{Artifacts: []portainer.Artifact{{Files: []portainer.ArtifactFile{{SourceID: srcID}}}}}
|
|
require.NoError(t, tx.Workflow().Create(wf))
|
|
require.NoError(t, tx.Stack().Create(&portainer.Stack{
|
|
ID: portainer.StackID(i),
|
|
Name: "stack-" + strconv.Itoa(i),
|
|
EndpointID: portainer.EndpointID(i),
|
|
WorkflowID: wf.ID,
|
|
}))
|
|
}
|
|
|
|
return tx.User().Create(&portainer.User{ID: 1, Role: portainer.AdministratorRole})
|
|
}))
|
|
|
|
var stats map[portainer.SourceID]SourceStats
|
|
require.NoError(t, store.ViewTx(func(tx dataservices.DataStoreTx) error {
|
|
var err error
|
|
_, stats, err = FetchSourceStats(tx, nil, adminContext())
|
|
|
|
return err
|
|
}))
|
|
|
|
st := stats[srcID]
|
|
require.Equal(t, 2, st.WorkflowCount)
|
|
require.Len(t, st.EndpointIDs, 2)
|
|
}
|
|
|
|
func TestFetchSourceStats_UnusedSourceHasZeroStats(t *testing.T) {
|
|
t.Parallel()
|
|
_, store := datastore.MustNewTestStore(t, false, true)
|
|
|
|
var unusedID portainer.SourceID
|
|
|
|
require.NoError(t, store.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
|
src := &portainer.Source{Name: "unused", Type: portainer.SourceTypeGit}
|
|
require.NoError(t, tx.Source().Create(src))
|
|
unusedID = src.ID
|
|
|
|
return tx.User().Create(&portainer.User{ID: 1, Role: portainer.AdministratorRole})
|
|
}))
|
|
|
|
var stats map[portainer.SourceID]SourceStats
|
|
require.NoError(t, store.ViewTx(func(tx dataservices.DataStoreTx) error {
|
|
var err error
|
|
_, stats, err = FetchSourceStats(tx, nil, adminContext())
|
|
|
|
return err
|
|
}))
|
|
|
|
st := stats[unusedID]
|
|
require.Zero(t, st.WorkflowCount)
|
|
require.Empty(t, st.EndpointIDs)
|
|
}
|