Add base tests & post test tweaks

This commit is contained in:
Tomaž Jerman
2020-11-27 11:19:15 +01:00
parent 0bcd620a18
commit 09d679fba7
39 changed files with 1192 additions and 94 deletions
+1 -1
View File
@@ -22,7 +22,7 @@ type (
//
// Encoding errors are passed via Ec.
Encoder interface {
Encode(ctx context.Context, c Rc) error
Encode(ctx context.Context, p Provider) error
}
PrepareEncoder interface {
+4 -37
View File
@@ -2,7 +2,6 @@ package envoy
import (
"context"
"sync"
)
type (
@@ -12,40 +11,8 @@ type (
)
// @todo errors!
func Encode(ctx context.Context, p Provider, ee ...Encoder) error {
var err error
rcc := make([]Rc, len(ee))
var wg sync.WaitGroup
wg.Add(len(ee))
for i, e := range ee {
rcc[i] = make(Rc)
go func() {
defer wg.Done()
eErr := e.Encode(ctx, rcc[i])
if err != nil {
err = eErr
}
}()
}
for {
rs, err := p.Next(ctx)
if err != nil {
return err
}
for _, rc := range rcc {
rc <- rs
}
if rs == nil {
break
}
}
wg.Wait()
return nil
func Encode(ctx context.Context, p Provider, e Encoder) error {
// @todo add support for multiple encoders at the same time.
// The issue occurs with routines and error handling...
return e.Encode(ctx, p)
}
+42 -4
View File
@@ -10,13 +10,21 @@ type (
*base
Res *types.Page
NsRef *Ref
ModRef *Ref
NsRef *Ref
ModRef *Ref
ParentRef *Ref
ModRefs RefSet
ChartRefs RefSet
}
)
func NewComposePage(pg *types.Page, nsRef, modRef string) *ComposePage {
r := &ComposePage{base: &base{}}
func NewComposePage(pg *types.Page, nsRef, modRef, parentRef string) *ComposePage {
r := &ComposePage{
base: &base{},
ModRefs: make(RefSet, 0, 10),
ChartRefs: make(RefSet, 0, 10),
}
r.SetResourceType(COMPOSE_PAGE_RESOURCE_TYPE)
r.Res = pg
@@ -27,6 +35,36 @@ func NewComposePage(pg *types.Page, nsRef, modRef string) *ComposePage {
r.ModRef = r.AddRef(COMPOSE_MODULE_RESOURCE_TYPE, modRef)
}
if parentRef != "" {
r.ParentRef = r.AddRef(COMPOSE_PAGE_RESOURCE_TYPE, parentRef)
}
for _, b := range pg.Blocks {
switch b.Kind {
case "RecordList":
id, _ := b.Options["module"].(string)
if id != "" {
r.ModRefs = append(r.ModRefs, r.AddRef(COMPOSE_MODULE_RESOURCE_TYPE, id))
}
case "Chart":
id, _ := b.Options["chart"].(string)
if id != "" {
r.ChartRefs = append(r.ChartRefs, r.AddRef(COMPOSE_CHART_RESOURCE_TYPE, id))
}
case "Metric":
mm, _ := b.Options["metrics"].([]interface{})
for _, m := range mm {
mops, _ := m.(map[string]interface{})
id, _ := mops["module"].(string)
if id != "" {
r.ModRefs = append(r.ModRefs, r.AddRef(COMPOSE_MODULE_RESOURCE_TYPE, id))
}
}
}
}
return r
}
+1 -1
View File
@@ -57,7 +57,7 @@ func (n *applicationState) Encode(ctx context.Context, s store.Storer, state *en
res.ID = n.app.ID
}
if res.ID <= 0 {
res.ID = nextID()
res.ID = NextID()
}
// This is not possible, but let's do it anyway
+11 -7
View File
@@ -2,6 +2,7 @@ package store
import (
"context"
"fmt"
"time"
"github.com/cortezaproject/corteza-server/compose/types"
@@ -45,11 +46,6 @@ func (n *composeChartState) Prepare(ctx context.Context, s store.Storer, state *
return composeNamespaceErrUnresolved(n.res.NsRef.Identifiers)
}
// Can't do anything else, since the NS doesn't yet exist
if n.relNS.ID <= 0 {
return nil
}
// Get related modules
n.relMods = make(types.ModuleSet, len(n.res.ModRef))
for i, mRef := range n.res.ModRef {
@@ -83,7 +79,7 @@ func (n *composeChartState) Encode(ctx context.Context, s store.Storer, state *e
res.ID = n.chr.ID
}
if res.ID <= 0 {
res.ID = nextID()
res.ID = NextID()
}
// This is not possible, but let's do it anyway
@@ -170,6 +166,10 @@ func findComposeChartRS(ctx context.Context, s store.Storer, nsID uint64, rr res
return ch, nil
}
if nsID <= 0 {
return nil, nil
}
// Go in the store
return findComposeChartS(ctx, s, nsID, makeGenericFilter(ii))
}
@@ -211,7 +211,7 @@ func findComposeChartR(rr resource.InterfaceSet, ii resource.Identifiers) (ch *t
return nil
}
if chRes.Identifiers().HasAny(ii) {
if cr.Identifiers().HasAny(ii) {
chRes = cr
}
return nil
@@ -224,3 +224,7 @@ func findComposeChartR(rr resource.InterfaceSet, ii resource.Identifiers) (ch *t
return nil
}
func composeChartErrUnresolved(ii resource.Identifiers) error {
return fmt.Errorf("compose chart unresolved %v", ii.StringSlice())
}
+16 -3
View File
@@ -107,7 +107,7 @@ func (n *composeModuleState) Encode(ctx context.Context, s store.Storer, state *
res.ID = n.mod.ID
}
if res.ID <= 0 {
res.ID = nextID()
res.ID = NextID()
}
if state.Conflicting {
@@ -126,8 +126,17 @@ func (n *composeModuleState) Encode(ctx context.Context, s store.Storer, state *
}
// Fields
off := make(types.ModuleFieldSet, 0)
if n.mod != nil && n.mod.Fields != nil {
off = n.mod.Fields
}
for i, f := range res.Fields {
f.ID = res.ID
of := off.FindByName(f.Name)
if of != nil {
f.ID = of.ID
} else {
f.ID = NextID()
}
f.ModuleID = res.ID
f.Place = i
f.DeletedAt = nil
@@ -262,6 +271,10 @@ func findComposeModuleRS(ctx context.Context, s store.Storer, nsID uint64, rr re
return mod, nil
}
if nsID <= 0 {
return nil, nil
}
// Go in the store
return findComposeModuleS(ctx, s, nsID, makeGenericFilter(ii))
}
@@ -303,7 +316,7 @@ func findComposeModuleR(rr resource.InterfaceSet, ii resource.Identifiers) (ns *
return nil
}
if modRes.Identifiers().HasAny(ii) {
if mr.Identifiers().HasAny(ii) {
modRes = mr
}
return nil
+1 -1
View File
@@ -55,7 +55,7 @@ func (n *composeNamespaceState) Encode(ctx context.Context, s store.Storer, stat
res.ID = n.ns.ID
}
if res.ID <= 0 {
res.ID = nextID()
res.ID = NextID()
}
// This is not possible, but let's do it anyway
+127 -10
View File
@@ -2,6 +2,7 @@ package store
import (
"context"
"fmt"
"time"
"github.com/cortezaproject/corteza-server/compose/types"
@@ -17,8 +18,12 @@ type (
res *resource.ComposePage
pg *types.Page
relNS *types.Namespace
relMod *types.Module
relNS *types.Namespace
relMod *types.Module
relParent *types.Page
relMods map[string]*types.Module
relCharts map[string]*types.Chart
}
)
@@ -27,6 +32,9 @@ func NewComposePageState(res *resource.ComposePage, cfg *EncoderConfig) resource
cfg: cfg,
res: res,
relMods: make(map[string]*types.Module),
relCharts: make(map[string]*types.Chart),
}
}
@@ -45,11 +53,6 @@ func (n *composePageState) Prepare(ctx context.Context, s store.Storer, state *e
return composeNamespaceErrUnresolved(n.res.NsRef.Identifiers)
}
// Can't do anything else, since the NS doesn't yet exist
if n.relNS.ID <= 0 {
return nil
}
// Get related module
// If this isn't a record page, there is no related module
if n.res.ModRef != nil {
@@ -62,6 +65,45 @@ func (n *composePageState) Prepare(ctx context.Context, s store.Storer, state *e
}
}
// Get parent page
if n.res.ParentRef != nil {
n.relParent, err = findComposePageRS(ctx, s, n.relNS.ID, state.ParentResources, n.res.ParentRef.Identifiers)
if err != nil {
return err
}
if n.relParent == nil {
return composePageErrUnresolved(n.res.ParentRef.Identifiers)
}
}
// Get other related modules
for _, mr := range n.res.ModRefs {
mod, err := findComposeModuleRS(ctx, s, n.relNS.ID, state.ParentResources, mr.Identifiers)
if err != nil {
return err
}
if mod == nil {
return composeModuleErrUnresolved(mr.Identifiers)
}
for id := range mr.Identifiers {
n.relMods[id] = mod
}
}
// Get related charts
for _, cr := range n.res.ChartRefs {
chr, err := findComposeChartRS(ctx, s, n.relNS.ID, state.ParentResources, cr.Identifiers)
if err != nil {
return err
}
if chr == nil {
return composeChartErrUnresolved(cr.Identifiers)
}
for id := range cr.Identifiers {
n.relCharts[id] = chr
}
}
// Try to get the original page
n.pg, err = findComposePageS(ctx, s, n.relNS.ID, makeGenericFilter(n.res.Identifiers()))
if err != nil {
@@ -83,7 +125,7 @@ func (n *composePageState) Encode(ctx context.Context, s store.Storer, state *en
res.ID = n.pg.ID
}
if res.ID <= 0 {
res.ID = nextID()
res.ID = NextID()
}
// This is not possible, but let's do it anyway
@@ -111,6 +153,73 @@ func (n *composePageState) Encode(ctx context.Context, s store.Storer, state *en
}
}
// Parent?
if n.res.ParentRef != nil {
res.SelfID = n.relParent.ID
if res.SelfID <= 0 {
mod := findComposePageR(state.ParentResources, n.res.ParentRef.Identifiers)
res.SelfID = mod.ID
}
}
// Blocks
getModID := func(id string) uint64 {
mod := n.relMods[id]
if mod == nil || mod.ID <= 0 {
mod = findComposeModuleR(state.ParentResources, resource.MakeIdentifiers(id))
if mod == nil || mod.ID <= 0 {
return 0
}
}
return mod.ID
}
for _, b := range res.Blocks {
switch b.Kind {
case "RecordList":
id, _ := b.Options["module"].(string)
if id == "" {
continue
}
mID := getModID(id)
if mID <= 0 {
return composeModuleErrUnresolved(resource.MakeIdentifiers(id))
}
b.Options["module"] = mID
case "Chart":
id, _ := b.Options["chart"].(string)
if id == "" {
continue
}
chr := n.relCharts[id]
if chr == nil || chr.ID <= 0 {
ii := resource.MakeIdentifiers(id)
chr = findComposeChartR(state.ParentResources, ii)
if chr == nil || chr.ID <= 0 {
return composeChartErrUnresolved(ii)
}
}
b.Options["chart"] = chr.ID
case "Metric":
mm, _ := b.Options["metrics"].([]interface{})
for _, m := range mm {
mops, _ := m.(map[string]interface{})
id, _ := mops["module"].(string)
if id == "" {
continue
}
mID := getModID(id)
if mID <= 0 {
return composeModuleErrUnresolved(resource.MakeIdentifiers(id))
}
mops["module"] = mID
}
}
}
// Create a fresh page
if !exists {
return store.CreateComposePage(ctx, s, res)
@@ -167,11 +276,15 @@ func mergeComposePage(a, b *types.Page) *types.Page {
//
// Provided resources are prioritized.
func findComposePageRS(ctx context.Context, s store.Storer, nsID uint64, rr resource.InterfaceSet, ii resource.Identifiers) (pg *types.Page, err error) {
pg = findComposePageR(ctx, rr, ii)
pg = findComposePageR(rr, ii)
if pg != nil {
return pg, nil
}
if nsID <= 0 {
return nil, nil
}
// Go in the store
return findComposePageS(ctx, s, nsID, makeGenericFilter(ii))
}
@@ -204,7 +317,7 @@ func findComposePageS(ctx context.Context, s store.Storer, nsID uint64, gf gener
}
// findComposePageR looks for the page in the resources
func findComposePageR(ctx context.Context, rr resource.InterfaceSet, ii resource.Identifiers) (pg *types.Page) {
func findComposePageR(rr resource.InterfaceSet, ii resource.Identifiers) (pg *types.Page) {
var pgRes *resource.ComposePage
rr.Walk(func(r resource.Interface) error {
@@ -225,3 +338,7 @@ func findComposePageR(ctx context.Context, rr resource.InterfaceSet, ii resource
}
return nil
}
func composePageErrUnresolved(ii resource.Identifiers) error {
return fmt.Errorf("compose page unresolved %v", ii.StringSlice())
}
+1 -1
View File
@@ -138,7 +138,7 @@ func (n *composeRecordState) Encode(ctx context.Context, s store.Storer, state *
if rec.ID <= 0 && exists {
rec.ID = rm[r.ID].ID
} else {
rec.ID = nextID()
rec.ID = NextID()
}
im[r.ID] = rec.ID
+5 -3
View File
@@ -124,11 +124,14 @@ func (se *storeEncoder) Prepare(ctx context.Context, ee ...*envoy.ResourceState)
}
// Encode encodes available resource states using the given store encoder
func (se *storeEncoder) Encode(ctx context.Context, rc envoy.Rc) error {
func (se *storeEncoder) Encode(ctx context.Context, p envoy.Provider) error {
var e *envoy.ResourceState
return store.Tx(ctx, se.s, func(ctx context.Context, s store.Storer) (err error) {
for {
e = <-rc
e, err = p.Next(ctx)
if err != nil {
return err
}
if e == nil {
return nil
}
@@ -142,7 +145,6 @@ func (se *storeEncoder) Encode(ctx context.Context, rc envoy.Rc) error {
if err != nil {
return se.WrapError("encode", e.Res, err)
}
}
})
+18 -13
View File
@@ -87,19 +87,25 @@ func (n *rbacRuleState) Encode(ctx context.Context, s store.Storer, state *envoy
rule.Resource = rule.Resource.AppendWildcard()
}
// There isn't anything to merge really, so Skip & MergeLeft skip it;
// Replace & MergeRight replace it.
//
// Replacing is handled after
switch n.cfg.OnExisting {
case Skip,
MergeLeft:
return nil
ee, _ := rbacRules.Filter(func(r *rbac.Rule) (bool, error) {
return (r.RoleID == rule.RoleID && r.Resource == rule.Resource && r.Operation == rule.Operation), nil
})
if len(ee) <= 0 {
rbacRules = rbacRules.Merge(rule)
} else {
// There isn't anything to merge really, so Skip & MergeLeft skip it;
// Replace & MergeRight replace it.
switch n.cfg.OnExisting {
case Skip,
MergeLeft:
return nil
}
rbacRules = rbacRules.Merge(rule)
}
nrr := rbacRules.Merge(rule)
d, u := nrr.Dirty()
d, u := rbacRules.Dirty()
err = store.DeleteRbacRule(ctx, s, d...)
if err != nil {
return
@@ -110,8 +116,7 @@ func (n *rbacRuleState) Encode(ctx context.Context, s store.Storer, state *envoy
return
}
nrr.Clear()
rbacRules = nrr
rbacRules.Clear()
return nil
}
+1 -1
View File
@@ -54,7 +54,7 @@ func (n *roleState) Encode(ctx context.Context, s store.Storer, state *envoy.Res
rl.ID = n.rl.ID
}
if rl.ID <= 0 {
rl.ID = nextID()
rl.ID = NextID()
}
// This is not possible, but let's do it anyway
+5 -2
View File
@@ -74,8 +74,11 @@ func (n *settingsState) Encode(ctx context.Context, s store.Storer, state *envoy
ss = append(ss, ns)
}
}
return store.UpsertSetting(ctx, s, ss...)
err = store.TruncateSettings(ctx, s)
if err != nil {
return err
}
return store.CreateSetting(ctx, s, ss...)
}
// mergeSettings merges b into a, prioritising a
+1 -1
View File
@@ -55,7 +55,7 @@ func (n *userState) Encode(ctx context.Context, s store.Storer, state *envoy.Res
res.ID = n.u.ID
}
if res.ID <= 0 {
res.ID = nextID()
res.ID = NextID()
}
// This is not possible, but let's do it anyway
+2 -2
View File
@@ -22,8 +22,8 @@ var (
// we'll use the pkg/handle to check for handles.
refy = regexp.MustCompile(`^[1-9](\d*)$`)
// wrapper around nextID that will aid service testing
nextID = func() uint64 {
// wrapper around NextID that will aid service testing
NextID = func() uint64 {
return id.Next()
}
)
+126
View File
@@ -0,0 +1,126 @@
package tests
import (
"context"
"os"
"path"
"github.com/cortezaproject/corteza-server/compose/types"
"github.com/cortezaproject/corteza-server/pkg/envoy"
"github.com/cortezaproject/corteza-server/pkg/envoy/resource"
es "github.com/cortezaproject/corteza-server/pkg/envoy/store"
"github.com/cortezaproject/corteza-server/pkg/envoy/yaml"
"github.com/cortezaproject/corteza-server/store"
"github.com/cortezaproject/corteza-server/store/sqlite3"
stypes "github.com/cortezaproject/corteza-server/system/types"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
)
type (
tc struct {
name string
suite string
file string
// Before the data gets processed
pre func() error
// After the data gets processed
post func(req *require.Assertions, err error)
// Data assertions
check func(req *require.Assertions)
}
)
func initStore(ctx context.Context) (store.Storer, error) {
s, err := sqlite3.ConnectInMemoryWithDebug(ctx)
if err != nil {
return nil, err
}
err = store.Upgrade(ctx, zap.NewNop(), s)
if err != nil {
return nil, err
}
return s, nil
}
func yd(ctx context.Context, suite, fname string) ([]resource.Interface, error) {
fp := path.Join("testdata", suite, fname)
f, err := os.Open(fp)
if err != nil {
return nil, err
}
defer f.Close()
info, err := os.Stat(fp)
if err != nil {
return nil, err
}
d := yaml.Decoder(nil)
return d.Decode(ctx, f, info)
}
func encode(ctx context.Context, s store.Storer, nn []resource.Interface) error {
se := es.NewStoreEncoder(s, nil)
bld := envoy.NewGraphBuilder(se)
g, err := bld.Build(ctx, nn...)
if err != nil {
return err
}
return envoy.Encode(ctx, g, se)
}
func storeNamespace(ctx context.Context, s store.Storer, nsID uint64, ss ...string) error {
ns := &types.Namespace{
ID: nsID,
}
if len(ss) > 0 {
ns.Slug = ss[0]
}
if len(ss) > 1 {
ns.Name = ss[1]
}
return store.CreateComposeNamespace(ctx, s, ns)
}
func storeModule(ctx context.Context, s store.Storer, nsID, modID uint64, ss ...string) error {
mod := &types.Module{
ID: modID,
NamespaceID: nsID,
}
if len(ss) > 0 {
mod.Handle = ss[0]
}
if len(ss) > 1 {
mod.Name = ss[1]
}
return store.CreateComposeModule(ctx, s, mod)
}
func storeRole(ctx context.Context, s store.Storer, rID uint64, ss ...string) error {
r := &stypes.Role{
ID: rID,
}
if len(ss) > 0 {
r.Handle = ss[0]
}
if len(ss) > 1 {
r.Name = ss[1]
}
return store.CreateRole(ctx, s, r)
}
// Helper to collect resulting errors, returning the first one
func ce(ee ...error) error {
for _, e := range ee {
if e != nil {
return e
}
}
return nil
}
+154
View File
@@ -0,0 +1,154 @@
package tests
import (
"context"
"fmt"
"testing"
"github.com/cortezaproject/corteza-server/compose/types"
su "github.com/cortezaproject/corteza-server/pkg/envoy/store"
"github.com/cortezaproject/corteza-server/store"
"github.com/stretchr/testify/require"
)
func TestModuleRels(t *testing.T) {
var (
ctx = context.Background()
s, err = initStore(ctx)
)
if err != nil {
t.Fatalf("failed to init sqlite in-memory db: %v", err)
}
ni := uint64(0)
su.NextID = func() uint64 {
ni++
return ni
}
prepare := func(ctx context.Context, s store.Storer, t *testing.T, suite, file string) (*require.Assertions, error) {
req := require.New(t)
nn, err := yd(ctx, suite, file)
req.NoError(err)
return req, encode(ctx, s, nn)
}
cases := []*tc{
{
name: "simple mods; self ref",
suite: "mod_rel",
file: "modules_self_ref",
pre: func() (err error) {
return ce(
s.TruncateComposeNamespaces(ctx),
s.TruncateComposeModules(ctx),
storeNamespace(ctx, s, 10, "ns1"),
)
},
post: func(req *require.Assertions, err error) {
req.NoError(err)
},
check: func(req *require.Assertions) {
mod, err := store.LookupComposeModuleByNamespaceIDHandle(ctx, s, 10, "mod1")
req.NoError(err)
req.NotNil(mod)
mod.Fields, _, err = store.SearchComposeModuleFields(ctx, s, types.ModuleFieldFilter{ModuleID: []uint64{mod.ID}})
req.NoError(err)
ff := mod.Fields
req.Len(ff, 1)
req.Equal("Record", ff[0].Kind)
req.Equal(mod.ID, uint64(ff[0].Options.Int64("module")))
},
},
{
name: "simple mods; ref to peer",
suite: "mod_rel",
file: "modules_peer_ref",
pre: func() (err error) {
return ce(
s.TruncateComposeNamespaces(ctx),
s.TruncateComposeModules(ctx),
s.TruncateComposeModuleFields(ctx),
storeNamespace(ctx, s, 100, "ns1"),
)
},
post: func(req *require.Assertions, err error) {
req.NoError(err)
},
check: func(req *require.Assertions) {
pmod, err := store.LookupComposeModuleByNamespaceIDHandle(ctx, s, 100, "mod2")
req.NoError(err)
req.NotNil(pmod)
mod, err := store.LookupComposeModuleByNamespaceIDHandle(ctx, s, 100, "mod1")
req.NoError(err)
req.NotNil(mod)
mod.Fields, _, err = store.SearchComposeModuleFields(ctx, s, types.ModuleFieldFilter{ModuleID: []uint64{mod.ID}})
req.NoError(err)
ff := mod.Fields
req.Len(ff, 1)
req.Equal("Record", ff[0].Kind)
req.Equal(pmod.ID, uint64(ff[0].Options.Int64("module")))
},
},
{
name: "simple mods; ref to store",
suite: "mod_rel",
file: "modules_store_ref",
pre: func() (err error) {
return ce(
s.TruncateComposeNamespaces(ctx),
s.TruncateComposeModules(ctx),
s.TruncateComposeModuleFields(ctx),
storeNamespace(ctx, s, 100, "ns1"),
storeModule(ctx, s, 100, 200, "mod2"),
)
},
post: func(req *require.Assertions, err error) {
req.NoError(err)
},
check: func(req *require.Assertions) {
smod, err := store.LookupComposeModuleByNamespaceIDHandle(ctx, s, 100, "mod2")
req.NoError(err)
req.NotNil(smod)
mod, err := store.LookupComposeModuleByNamespaceIDHandle(ctx, s, 100, "mod1")
req.NoError(err)
req.NotNil(mod)
mod.Fields, _, err = store.SearchComposeModuleFields(ctx, s, types.ModuleFieldFilter{ModuleID: []uint64{mod.ID}})
req.NoError(err)
ff := mod.Fields
req.Len(ff, 1)
req.Equal("Record", ff[0].Kind)
req.Equal(smod.ID, uint64(ff[0].Options.Int64("module")))
},
},
}
for _, c := range cases {
t.Run(fmt.Sprintf("%s; %s/%s", c.name, c.suite, c.file), func(t *testing.T) {
err = c.pre()
if err != nil {
t.Fatal(err.Error())
}
req, err := prepare(ctx, s, t, c.suite, c.file+".yaml")
c.post(req, err)
c.check(req)
})
ni = 0
}
}
+453
View File
@@ -0,0 +1,453 @@
package tests
import (
"context"
"fmt"
"strings"
"testing"
"github.com/cortezaproject/corteza-server/compose/types"
su "github.com/cortezaproject/corteza-server/pkg/envoy/store"
"github.com/cortezaproject/corteza-server/pkg/rbac"
"github.com/cortezaproject/corteza-server/store"
st "github.com/cortezaproject/corteza-server/system/types"
"github.com/stretchr/testify/require"
)
func TestSimpleCases(t *testing.T) {
var (
ctx = context.Background()
s, err = initStore(ctx)
)
if err != nil {
t.Fatalf("failed to init sqlite in-memory db: %v", err)
}
ni := uint64(0)
su.NextID = func() uint64 {
ni++
return ni
}
prepare := func(ctx context.Context, s store.Storer, t *testing.T, suite, file string) (*require.Assertions, error) {
req := require.New(t)
nn, err := yd(ctx, suite, file)
req.NoError(err)
return req, encode(ctx, s, nn)
}
cases := []*tc{
{
name: "simple namespaces",
suite: "simple",
file: "namespaces",
pre: func() (err error) {
return s.TruncateComposeNamespaces(ctx)
},
post: func(req *require.Assertions, err error) {
req.NoError(err)
},
check: func(req *require.Assertions) {
ns, err := store.LookupComposeNamespaceBySlug(ctx, s, "ns1")
req.NoError(err)
req.NotNil(ns)
req.Equal("ns1", ns.Slug)
req.Equal("ns1 name", ns.Name)
},
},
{
name: "simple mods; no namespace",
suite: "simple",
file: "modules_no_ns",
pre: func() (err error) {
return ce(
s.TruncateComposeNamespaces(ctx),
s.TruncateComposeModules(ctx),
s.TruncateComposeModuleFields(ctx),
)
},
post: func(req *require.Assertions, err error) {
req.Error(err)
req.True(strings.Contains(err.Error(), "prepare compose module"))
req.True(strings.Contains(err.Error(), "compose namespace unresolved"))
},
check: func(req *require.Assertions) {},
},
{
name: "simple mods",
suite: "simple",
file: "modules",
pre: func() (err error) {
return ce(
s.TruncateComposeNamespaces(ctx),
s.TruncateComposeModules(ctx),
s.TruncateComposeModuleFields(ctx),
storeNamespace(ctx, s, 100, "ns1"),
)
},
post: func(req *require.Assertions, err error) {
req.NoError(err)
},
check: func(req *require.Assertions) {
mod, err := store.LookupComposeModuleByNamespaceIDHandle(ctx, s, 100, "mod1")
req.NoError(err)
req.NotNil(mod)
mod.Fields, _, err = store.SearchComposeModuleFields(ctx, s, types.ModuleFieldFilter{ModuleID: []uint64{mod.ID}})
req.NoError(err)
req.Equal("mod1", mod.Handle)
req.Equal("mod1 name", mod.Name)
req.Equal(uint64(100), mod.NamespaceID)
ff := mod.Fields
req.Len(ff, 1)
req.Equal("f1", ff[0].Name)
req.Equal("f1 label", ff[0].Label)
req.Equal("String", ff[0].Kind)
},
},
{
name: "simple charts; no ns",
suite: "simple",
file: "charts_no_ns",
pre: func() (err error) {
return ce(
s.TruncateComposeNamespaces(ctx),
s.TruncateComposeModules(ctx),
s.TruncateComposeCharts(ctx),
)
},
post: func(req *require.Assertions, err error) {
req.Error(err)
req.True(strings.Contains(err.Error(), "prepare compose chart"))
req.True(strings.Contains(err.Error(), "compose namespace unresolved"))
},
check: func(req *require.Assertions) {},
},
{
name: "simple charts; no mod",
suite: "simple",
file: "charts_no_mod",
pre: func() (err error) {
return ce(
s.TruncateComposeNamespaces(ctx),
s.TruncateComposeModules(ctx),
s.TruncateComposeCharts(ctx),
storeNamespace(ctx, s, 100, "ns1"),
)
},
post: func(req *require.Assertions, err error) {
req.Error(err)
req.True(strings.Contains(err.Error(), "prepare compose chart"))
req.True(strings.Contains(err.Error(), "compose module unresolved"))
},
check: func(req *require.Assertions) {},
},
{
name: "simple charts",
suite: "simple",
file: "charts",
pre: func() (err error) {
return ce(
s.TruncateComposeNamespaces(ctx),
s.TruncateComposeModules(ctx),
s.TruncateComposeCharts(ctx),
storeNamespace(ctx, s, 100, "ns1"),
storeModule(ctx, s, 100, 200, "mod1"),
)
},
post: func(req *require.Assertions, err error) {
req.NoError(err)
},
check: func(req *require.Assertions) {
chr, err := store.LookupComposeChartByNamespaceIDHandle(ctx, s, 100, "c1")
req.NoError(err)
req.NotNil(chr)
req.Equal("c1", chr.Handle)
req.Equal("c1 name", chr.Name)
req.Len(chr.Config.Reports, 1)
req.Equal(uint64(200), chr.Config.Reports[0].ModuleID)
},
},
{
name: "simple pages; no ns",
suite: "simple",
file: "pages_no_ns",
pre: func() (err error) {
return ce(
s.TruncateComposeNamespaces(ctx),
s.TruncateComposeModules(ctx),
s.TruncateComposeCharts(ctx),
s.TruncateComposePages(ctx),
)
},
post: func(req *require.Assertions, err error) {
req.Error(err)
req.True(strings.Contains(err.Error(), "prepare compose page"))
req.True(strings.Contains(err.Error(), "compose namespace unresolved"))
},
check: func(req *require.Assertions) {},
},
{
name: "record pages; no mod",
suite: "simple",
file: "pages_r_no_mod",
pre: func() (err error) {
return ce(
s.TruncateComposeNamespaces(ctx),
s.TruncateComposeModules(ctx),
s.TruncateComposeCharts(ctx),
s.TruncateComposePages(ctx),
storeNamespace(ctx, s, 100, "ns1"),
)
},
post: func(req *require.Assertions, err error) {
req.Error(err)
req.True(strings.Contains(err.Error(), "prepare compose page"))
req.True(strings.Contains(err.Error(), "compose module unresolved"))
},
check: func(req *require.Assertions) {},
},
{
name: "simple pages",
suite: "simple",
file: "pages",
pre: func() (err error) {
return ce(
s.TruncateComposeNamespaces(ctx),
s.TruncateComposeModules(ctx),
s.TruncateComposeCharts(ctx),
storeNamespace(ctx, s, 100, "ns1"),
)
},
post: func(req *require.Assertions, err error) {
req.NoError(err)
},
check: func(req *require.Assertions) {
pg, err := store.LookupComposePageByNamespaceIDHandle(ctx, s, 100, "pg1")
req.NoError(err)
req.NotNil(pg)
req.Equal("pg1", pg.Handle)
req.Equal("pg1 name", pg.Title)
req.Equal(uint64(0), pg.ModuleID)
req.Equal(uint64(100), pg.NamespaceID)
req.Len(pg.Blocks, 1)
req.Equal("block1", pg.Blocks[0].Title)
},
},
{
name: "record page",
suite: "simple",
file: "pages_r",
pre: func() (err error) {
return ce(
s.TruncateComposeNamespaces(ctx),
s.TruncateComposeModules(ctx),
s.TruncateComposeCharts(ctx),
s.TruncateComposePages(ctx),
storeNamespace(ctx, s, 100, "ns1"),
storeModule(ctx, s, 100, 200, "mod1"),
)
},
post: func(req *require.Assertions, err error) {
req.NoError(err)
},
check: func(req *require.Assertions) {
pg, err := store.LookupComposePageByNamespaceIDHandle(ctx, s, 100, "pg1")
req.NoError(err)
req.NotNil(pg)
req.Equal("pg1", pg.Handle)
req.Equal("pg1 name", pg.Title)
req.Equal(uint64(100), pg.NamespaceID)
req.Equal(uint64(200), pg.ModuleID)
req.Len(pg.Blocks, 1)
req.Equal("block1", pg.Blocks[0].Title)
},
},
{
name: "applications",
suite: "simple",
file: "applications",
pre: func() (err error) {
return ce(
s.TruncateApplications(ctx),
)
},
post: func(req *require.Assertions, err error) {
req.NoError(err)
},
check: func(req *require.Assertions) {
apps, _, err := store.SearchApplications(ctx, s, st.ApplicationFilter{
Name: "app1",
})
req.NoError(err)
req.NotNil(apps)
req.Len(apps, 1)
app := apps[0]
req.Equal("app1", app.Name)
},
},
{
name: "users",
suite: "simple",
file: "users",
pre: func() (err error) {
return ce(
s.TruncateUsers(ctx),
)
},
post: func(req *require.Assertions, err error) {
req.NoError(err)
},
check: func(req *require.Assertions) {
u, err := store.LookupUserByHandle(ctx, s, "u1")
req.NoError(err)
req.NotNil(u)
req.Equal("u1", u.Handle)
req.Equal("u1 name", u.Name)
req.Equal("u1@example.tld", u.Email)
},
},
{
name: "roles",
suite: "simple",
file: "roles",
pre: func() (err error) {
return ce(
s.TruncateRoles(ctx),
)
},
post: func(req *require.Assertions, err error) {
req.NoError(err)
},
check: func(req *require.Assertions) {
r, err := store.LookupRoleByHandle(ctx, s, "r1")
req.NoError(err)
req.NotNil(r)
req.Equal("r1", r.Handle)
req.Equal("r1 name", r.Name)
},
},
{
name: "settings",
suite: "simple",
file: "settings",
pre: func() (err error) {
return ce(
s.TruncateSettings(ctx),
)
},
post: func(req *require.Assertions, err error) {
req.NoError(err)
},
check: func(req *require.Assertions) {
ss, _, err := store.SearchSettings(ctx, s, st.SettingsFilter{})
req.NoError(err)
req.NotNil(ss)
req.Len(ss, 3)
},
},
{
name: "rbac rules; no role",
suite: "simple",
file: "rbac_rules_no_role",
pre: func() (err error) {
return ce(
s.TruncateRoles(ctx),
s.TruncateRbacRules(ctx),
s.TruncateComposeNamespaces(ctx),
s.TruncateComposeModules(ctx),
s.TruncateComposeModuleFields(ctx),
)
},
post: func(req *require.Assertions, err error) {
req.Error(err)
req.True(strings.Contains(err.Error(), "prepare rbac rule"))
req.True(strings.Contains(err.Error(), "role unresolved"))
},
check: func(req *require.Assertions) {
},
},
{
name: "rbac rules",
suite: "simple",
file: "rbac_rules",
pre: func() (err error) {
return ce(
s.TruncateRoles(ctx),
s.TruncateRbacRules(ctx),
s.TruncateComposeNamespaces(ctx),
s.TruncateComposeModules(ctx),
s.TruncateComposeModuleFields(ctx),
storeRole(ctx, s, 100, "r1"),
)
},
post: func(req *require.Assertions, err error) {
req.NoError(err)
},
check: func(req *require.Assertions) {
rr, _, err := store.SearchRbacRules(ctx, s, rbac.RuleFilter{})
req.NoError(err)
req.NotNil(rr)
req.Len(rr, 3)
},
},
}
for _, c := range cases {
t.Run(fmt.Sprintf("%s; %s/%s", c.name, c.suite, c.file), func(t *testing.T) {
err = c.pre()
if err != nil {
t.Fatal(err.Error())
}
req, err := prepare(ctx, s, t, c.suite, c.file+".yaml")
c.post(req, err)
c.check(req)
})
ni = 0
}
}
+13
View File
@@ -0,0 +1,13 @@
namespace: ns1
modules:
mod1:
name: mod1 name
fields:
f1:
label: f1 label
kind: Record
required: true
options:
module: mod2
mod2:
name: mod2 name
+11
View File
@@ -0,0 +1,11 @@
namespace: ns1
modules:
mod1:
name: mod1 name
fields:
f1:
label: f1 label
kind: Record
required: true
options:
module: mod1
+11
View File
@@ -0,0 +1,11 @@
namespace: ns1
modules:
mod1:
name: mod1 name
fields:
f1:
label: f1 label
kind: Record
required: true
options:
module: mod2
+2
View File
@@ -0,0 +1,2 @@
applications:
- app1
+26
View File
@@ -0,0 +1,26 @@
namespace: ns1
charts:
c1:
name: c1 name
config:
reports:
- dimensions:
- conditions: {}
field: Rating
meta: {}
modifier: (no grouping / buckets)
skipMissing: true
filter: ""
metrics:
- aggregate: AVG
field: count
fill: true
fixTooltips: false
label: Number of Accounts
lineTension: 0.4
type: line
module: mod1
renderer: {}
yAxis:
label: Accounts
colorScheme: tableau.Tableau10
+26
View File
@@ -0,0 +1,26 @@
namespace: ns1
charts:
c1:
name: c1 name
config:
reports:
- dimensions:
- conditions: {}
field: Rating
meta: {}
modifier: (no grouping / buckets)
skipMissing: true
filter: ""
metrics:
- aggregate: AVG
field: count
fill: true
fixTooltips: false
label: Number of Accounts
lineTension: 0.4
type: line
module: notThere
renderer: {}
yAxis:
label: Accounts
colorScheme: tableau.Tableau10
+26
View File
@@ -0,0 +1,26 @@
namespace: notThere
charts:
c1:
name: c1 name
config:
reports:
- dimensions:
- conditions: {}
field: Rating
meta: {}
modifier: (no grouping / buckets)
skipMissing: true
filter: ""
metrics:
- aggregate: AVG
field: count
fill: true
fixTooltips: false
label: Number of Accounts
lineTension: 0.4
type: line
module: mod1
renderer: {}
yAxis:
label: Accounts
colorScheme: tableau.Tableau10
+9
View File
@@ -0,0 +1,9 @@
namespace: ns1
modules:
mod1:
name: mod1 name
fields:
f1:
label: f1 label
kind: String
required: true
+9
View File
@@ -0,0 +1,9 @@
namespace: notThere
modules:
mod1:
name: mod1 name
fields:
AccountName:
label: REP
kind: String
required: true
+3
View File
@@ -0,0 +1,3 @@
namespaces:
ns1:
name: ns1 name
+6
View File
@@ -0,0 +1,6 @@
namespace: ns1
pages:
pg1:
title: pg1 name
blocks:
- title: block1
+7
View File
@@ -0,0 +1,7 @@
namespace: notThere
pages:
pg1:
title: pg1 name
blocks:
- title: block1
+7
View File
@@ -0,0 +1,7 @@
namespace: ns1
pages:
pg1:
title: pg1 name
module: mod1
blocks:
- title: block1
+8
View File
@@ -0,0 +1,8 @@
namespace: ns1
pages:
pg1:
title: pg1 name
module: notThere
blocks:
- title: block1
+11
View File
@@ -0,0 +1,11 @@
allow:
r1:
compose:
- access
deny:
r1:
compose:namespace:
- access
messaging:
- access
+11
View File
@@ -0,0 +1,11 @@
allow:
r1:
compose:
- access
deny:
r1:
compose:namespace:
- access
messaging:
- access
+3
View File
@@ -0,0 +1,3 @@
roles:
r1:
name: r1 name
+4
View File
@@ -0,0 +1,4 @@
settings:
privacy.mask.email: true
privacy.mask.name: true
auth.mail.email-confirmation.subject.en: Test
+4
View File
@@ -0,0 +1,4 @@
users:
u1:
name: u1 name
email: u1@example.tld
+26 -4
View File
@@ -11,11 +11,14 @@ import (
type (
composePage struct {
res *types.Page
children composePageSet
res *types.Page
children composePageSet
refNamespace string
refModule string
rbac rbacRuleSet
refParent string
rbac rbacRuleSet
}
composePageSet []*composePage
@@ -59,6 +62,9 @@ func (wset composePageSet) setNamespaceRef(ref string) error {
}
res.refNamespace = ref
if res.children != nil {
res.children.setNamespaceRef(ref)
}
}
return nil
@@ -131,11 +137,27 @@ func (wrap *composePage) UnmarshalYAML(n *yaml.Node) (err error) {
}
func (wrap composePage) MarshalEnvoy() ([]resource.Interface, error) {
rs := resource.NewComposePage(wrap.res, wrap.refNamespace, wrap.refModule)
rs := resource.NewComposePage(wrap.res, wrap.refNamespace, wrap.refModule, wrap.refParent)
return envoy.CollectNodes(
rs,
wrap.children.bindParent(rs),
wrap.rbac.bindResource(rs),
// @todo Not sure yet
// wrap.children,
)
}
func (rr composePageSet) bindParent(res resource.Interface) composePageSet {
rtr := make(composePageSet, 0, len(rr))
for _, r := range rr {
idd := res.Identifiers().StringSlice()
if len(idd) > 0 {
r.refParent = idd[0]
}
rtr = append(rtr, r)
}
return rtr
}
-3
View File
@@ -72,9 +72,6 @@ func (doc *Document) Decode(ctx context.Context, l loader) ([]resource.Interface
if doc.rbac != nil {
mm = append(mm, doc.rbac)
}
if doc.users != nil {
mm = append(mm, doc.users)
}
for _, m := range mm {
if tmp, err := m.MarshalEnvoy(); err != nil {