feat(sources): add sources and workflows to the backend BE-12919 (#2666)
This commit is contained in:
@@ -0,0 +1,263 @@
|
||||
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, GitConfig: cfg}
|
||||
require.NoError(t, tx.Source().Create(src))
|
||||
|
||||
wf := &portainer.Workflow{SourceIDs: []portainer.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})
|
||||
}))
|
||||
|
||||
items, err := FetchWorkflows(t.Context(), store, nil, nil, adminContext(), nil)
|
||||
require.NoError(t, 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})
|
||||
}))
|
||||
|
||||
items, err := FetchWorkflows(t.Context(), store, nil, nil, adminContext(), set.ToSet([]portainer.EndpointID{1, 2}))
|
||||
require.NoError(t, 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})
|
||||
}))
|
||||
|
||||
items, err := FetchWorkflows(t.Context(), store, nil, nil, adminContext(), nil)
|
||||
require.NoError(t, 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})
|
||||
}))
|
||||
|
||||
items, err := FetchWorkflows(t.Context(), store, nil, nil, adminContext(), nil)
|
||||
require.NoError(t, 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{SourceIDs: []portainer.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)
|
||||
}
|
||||
Reference in New Issue
Block a user