snapshot of https://github.com/cortezaproject/corteza.git (2024.9.x, 2026-09-18; blobs > 131072B omitted)
Some checks failed
SonarQube / SonarQube (push) Failing after 5m28s
E2e test on main branch push / server and client test (map[name:admin port:8080]) (push) Failing after 3m33s
Server and lib tests / Library test (js) (push) Failing after 1m36s
Server and lib tests / Library test (vue) (push) Successful in 2m24s
Server and lib tests / Web client test (admin) (push) Successful in 2m52s
Server and lib tests / Web client test (compose) (push) Successful in 3m23s
Server and lib tests / Web client test (discovery) (push) Successful in 2m49s
Server and lib tests / Web client test (one) (push) Successful in 2m52s
Server and lib tests / Web client test (privacy) (push) Successful in 2m44s
Server and lib tests / Web client test (reporter) (push) Successful in 2m56s
Server and lib tests / Web client test (workflow) (push) Successful in 3m0s
Server and lib tests / Server tests (push) Failing after 4m55s
E2e test on main branch push / notify-failure (push) Failing after 9s
Server and lib tests / Send matrix testing notification (push) Successful in 2s
Mark stale issues and pull requests / stale (push) Successful in 40s

This commit is contained in:
upstream-mirror
2026-09-18 01:22:23 +05:00
commit 7e385fc126
8391 changed files with 1766236 additions and 0 deletions

View File

@@ -0,0 +1,388 @@
package envoy
import (
"context"
"fmt"
"os"
"testing"
automationTypes "github.com/cortezaproject/corteza/server/automation/types"
"github.com/cortezaproject/corteza/server/compose/types"
"github.com/cortezaproject/corteza/server/pkg/envoyx"
"github.com/cortezaproject/corteza/server/pkg/filter"
"github.com/cortezaproject/corteza/server/store"
systemTypes "github.com/cortezaproject/corteza/server/system/types"
"github.com/davecgh/go-spew/spew"
"github.com/stretchr/testify/require"
)
func TestImportExport(t *testing.T) {
var (
ctx = context.Background()
req = require.New(t)
nodes envoyx.NodeSet
gg *envoyx.DepGraph
err error
)
cleanup(t)
// The test
//
// * imports some YAML files
// * checks the DB state
// * exports the DB into a YAML
// * clears the DB
// * imports the YAML
// * checks the DB state the same way as before
//
// The above outlined flow allows us to trivially check if the data is both
// imported and exported correctly.
//
// The initial step could also manually populate the DB but the YAML import
// is more convenient.
t.Run("initial import", func(t *testing.T) {
t.Run("parse configs", func(t *testing.T) {
nodes, _, err = defaultEnvoy.Decode(ctx, envoyx.DecodeParams{
Type: envoyx.DecodeTypeURI,
Params: map[string]any{
"uri": "file://testdata/full",
},
})
req.NoError(err)
})
t.Run("bake", func(t *testing.T) {
gg, err = defaultEnvoy.Bake(ctx, envoyx.EncodeParams{
Type: envoyx.EncodeTypeStore,
Params: map[string]any{
"storer": defaultStore,
"dal": defaultDal,
},
}, nil, nodes...)
req.NoError(err)
})
t.Run("import into DB", func(t *testing.T) {
err = defaultEnvoy.Encode(ctx, envoyx.EncodeParams{
Type: envoyx.EncodeTypeStore,
Params: map[string]any{
"storer": defaultStore,
"dal": defaultDal,
},
}, gg)
req.NoError(err)
})
assertFullState(ctx, t, defaultStore, req)
})
// Prepare a temp file where we'll dump the YAML into
auxFile, err := os.CreateTemp(os.TempDir(), "*.yaml")
req.NoError(err)
spew.Dump(auxFile.Name())
// defer os.Remove(auxFile.Name())
defer auxFile.Close()
t.Run("export", func(t *testing.T) {
t.Run("export from DB", func(t *testing.T) {
nodes, _, err = defaultEnvoy.Decode(ctx, envoyx.DecodeParams{
Type: envoyx.DecodeTypeStore,
Params: map[string]any{
"storer": defaultStore,
"dal": defaultDal,
},
Filter: map[string]envoyx.ResourceFilter{
types.ChartResourceType: {},
types.ModuleResourceType: {},
types.NamespaceResourceType: {},
types.PageResourceType: {},
types.PageLayoutResourceType: {},
systemTypes.ApplicationResourceType: {},
systemTypes.ApigwRouteResourceType: {},
systemTypes.AuthClientResourceType: {},
systemTypes.QueueResourceType: {},
systemTypes.ReportResourceType: {},
systemTypes.RoleResourceType: {},
systemTypes.TemplateResourceType: {},
systemTypes.UserResourceType: {},
systemTypes.DalConnectionResourceType: {},
systemTypes.DalSensitivityLevelResourceType: {},
automationTypes.WorkflowResourceType: {},
},
})
req.NoError(err)
})
t.Run("bake", func(t *testing.T) {
gg, err = defaultEnvoy.Bake(ctx, envoyx.EncodeParams{
Type: envoyx.EncodeTypeStore,
Params: map[string]any{
"storer": defaultStore,
"dal": defaultDal,
},
}, nil, nodes...)
req.NoError(err)
})
t.Run("write file", func(t *testing.T) {
err = defaultEnvoy.Encode(ctx, envoyx.EncodeParams{
Type: envoyx.EncodeTypeIo,
Params: map[string]any{
"writer": auxFile,
},
}, gg)
req.NoError(err)
})
})
cleanup(t)
t.Run("second import", func(t *testing.T) {
t.Run("yaml parse", func(t *testing.T) {
nodes, _, err = defaultEnvoy.Decode(ctx, envoyx.DecodeParams{
Type: envoyx.DecodeTypeURI,
Params: map[string]any{
"uri": fmt.Sprintf("file://%s", auxFile.Name()),
},
})
req.NoError(err)
})
t.Run("bake", func(t *testing.T) {
gg, err = defaultEnvoy.Bake(ctx, envoyx.EncodeParams{
Type: envoyx.EncodeTypeStore,
Params: map[string]any{
"storer": defaultStore,
"dal": defaultDal,
},
}, nil, nodes...)
req.NoError(err)
})
t.Run("run import", func(t *testing.T) {
err = defaultEnvoy.Encode(ctx, envoyx.EncodeParams{
Type: envoyx.EncodeTypeStore,
Params: map[string]any{
"storer": defaultStore,
"dal": defaultDal,
},
}, gg)
req.NoError(err)
})
assertFullState(ctx, t, defaultStore, req)
})
}
func assertFullState(ctx context.Context, t *testing.T, s store.Storer, req *require.Assertions) {
t.Run("check state", func(t *testing.T) {
t.Run("corteza::compose", func(t *testing.T) {
// Namespaces
ns, err := store.LookupComposeNamespaceBySlug(ctx, s, "test_ns_1")
req.NoError(err)
req.NotNil(ns)
// Modules
modules, _, err := store.SearchComposeModules(ctx, s, types.ModuleFilter{
NamespaceID: ns.ID,
Sorting: filter.Sorting{
Sort: filter.SortExprSet{{Column: "handle", Descending: false}},
},
})
req.NoError(err)
mod1 := modules.FindByHandle("test_ns_1_mod_1")
req.NotNil(mod1)
mod2 := modules.FindByHandle("test_ns_1_mod_2")
req.NotNil(mod2)
// Fields
fields, _, err := store.SearchComposeModuleFields(ctx, s, types.ModuleFieldFilter{
ModuleID: []uint64{mod1.ID, mod2.ID},
})
req.NoError(err)
assignModuleFields(modules, fields)
req.Len(mod1.Fields, 2)
req.Len(mod2.Fields, 2)
req.Equal(mod1.ID, mod2.Fields.FindByName("test_ns_1_mod_2_f1").Options.UInt64("moduleID"))
req.Equal(mod2.ID, mod2.Fields.FindByName("test_ns_1_mod_2_f2").Options.UInt64("moduleID"))
// Charts
charts, _, err := store.SearchComposeCharts(ctx, s, types.ChartFilter{
NamespaceID: ns.ID,
})
req.NoError(err)
ch1 := charts.FindByHandle("test_ns_1_chart_1")
req.NotNil(ch1)
req.Equal(mod1.ID, ch1.Config.Reports[0].ModuleID)
req.Equal(mod2.ID, ch1.Config.Reports[1].ModuleID)
// Pages
pages, _, err := store.SearchComposePages(ctx, s, types.PageFilter{
NamespaceID: ns.ID,
})
req.NoError(err)
pg1 := pages.FindByHandle("test_ns_1_page_1")
req.NotNil(pg1)
req.Len(pg1.Blocks, 8)
// @todo test page block references
// spew.Dump(pg1.Blocks)
rpg1 := pages.FindByHandle("test_ns_1_record_page_1")
req.NotNil(rpg1)
req.Equal(mod1.ID, rpg1.ModuleID)
// Page layouts
layouts, _, err := store.SearchComposePageLayouts(ctx, s, types.PageLayoutFilter{
NamespaceID: ns.ID,
})
req.NoError(err)
ly1 := layouts.FindByHandle("test_ns_1_page_1_layout_1")
req.NotNil(ly1)
ly2 := layouts.FindByHandle("test_ns_1_page_1_layout_2")
req.NotNil(ly2)
// Record page layouts
ly3 := layouts.FindByHandle("test_ns_1_record_page_1_layout_1")
req.NotNil(ly3)
ly4 := layouts.FindByHandle("test_ns_1_record_page_1_layout_2")
req.NotNil(ly4)
})
t.Run("corteza::system", func(t *testing.T) {
// Users
users, _, err := store.SearchUsers(ctx, s, systemTypes.UserFilter{})
req.NoError(err)
req.Len(users, 1)
u1 := users[0]
req.Equal("test_user_1", u1.Handle)
// ApigwRoutes
apigwroutes, _, err := store.SearchApigwRoutes(ctx, s, systemTypes.ApigwRouteFilter{})
req.NoError(err)
req.Len(apigwroutes, 1)
r1 := apigwroutes[0]
req.Equal("/test/endpoint/1", r1.Endpoint)
req.Equal(u1.ID, r1.CreatedBy)
req.Equal(u1.ID, r1.UpdatedBy)
// ApigwFilters
apigwfilter, _, err := store.SearchApigwFilters(ctx, s, systemTypes.ApigwFilterFilter{})
req.NoError(err)
req.Len(apigwfilter, 1)
f1 := apigwfilter[0]
req.Equal(r1.ID, f1.Route)
// AuthClients
clients, _, err := store.SearchAuthClients(ctx, s, systemTypes.AuthClientFilter{})
req.NoError(err)
req.Len(clients, 1)
c1 := clients[0]
req.Equal("test_authclient_1", c1.Handle)
req.Equal(u1.ID, c1.OwnedBy)
req.Equal(u1.ID, c1.CreatedBy)
req.Equal(u1.ID, c1.UpdatedBy)
// Queues
queues, _, err := store.SearchQueues(ctx, s, systemTypes.QueueFilter{})
req.NoError(err)
req.Len(queues, 1)
q1 := queues[0]
req.Equal("test_queue_1", q1.Queue)
req.Equal(u1.ID, q1.CreatedBy)
req.Equal(u1.ID, q1.UpdatedBy)
// Reports
reports, _, err := store.SearchReports(ctx, s, systemTypes.ReportFilter{})
req.NoError(err)
req.Len(reports, 1)
rp1 := reports[0]
req.Equal("test_report_1", rp1.Handle)
req.Equal(u1.ID, rp1.OwnedBy)
req.Equal(u1.ID, rp1.CreatedBy)
req.Equal(u1.ID, rp1.UpdatedBy)
// Roles
roles, _, err := store.SearchRoles(ctx, s, systemTypes.RoleFilter{})
req.NoError(err)
rl1 := roles[0]
req.Equal("test_role_1", rl1.Handle)
// Templates
templates, _, err := store.SearchTemplates(ctx, s, systemTypes.TemplateFilter{})
req.NoError(err)
req.Len(templates, 1)
tpl1 := templates[0]
req.Equal("test_template_1", tpl1.Handle)
req.Equal(u1.ID, tpl1.OwnerID)
// DalConnections
connections, _, err := store.SearchDalConnections(ctx, s, systemTypes.DalConnectionFilter{})
req.NoError(err)
req.Len(connections, 1)
conn1 := connections[0]
req.Equal("test_connection_1", conn1.Handle)
req.Equal(u1.ID, conn1.CreatedBy)
req.Equal(u1.ID, conn1.UpdatedBy)
// DalSensitivityLevels
dalsensitivitylevels, _, err := store.SearchDalSensitivityLevels(ctx, s, systemTypes.DalSensitivityLevelFilter{})
req.NoError(err)
req.Len(dalsensitivitylevels, 1)
sens1 := dalsensitivitylevels[0]
req.Equal("test_sensitivity_level_1", sens1.Handle)
req.Equal(u1.ID, sens1.CreatedBy)
req.Equal(u1.ID, sens1.UpdatedBy)
})
t.Run("corteza::automation", func(t *testing.T) {
// Workflows
workflows, _, err := store.SearchAutomationWorkflows(ctx, s, automationTypes.WorkflowFilter{})
req.NoError(err)
req.Len(workflows, 1)
wf1 := workflows[0]
req.Equal("test_workflow_1", wf1.Handle)
// Triggers
triggers, _, err := store.SearchAutomationTriggers(ctx, s, automationTypes.TriggerFilter{})
req.NoError(err)
req.Len(triggers, 1)
t1 := triggers[0]
req.Equal(wf1.ID, t1.WorkflowID)
})
})
}
func assignModuleFields(modules types.ModuleSet, fields types.ModuleFieldSet) {
for _, m := range modules {
for _, f := range fields {
if m.ID == f.ModuleID {
m.Fields = append(m.Fields, f)
}
}
}
}

View File

@@ -0,0 +1,191 @@
package envoy
import (
"context"
"os"
"testing"
"time"
automationEnvoy "github.com/cortezaproject/corteza/server/automation/envoy"
composeEnvoy "github.com/cortezaproject/corteza/server/compose/envoy"
systemEnvoy "github.com/cortezaproject/corteza/server/system/envoy"
"github.com/cortezaproject/corteza/server/tests/helpers"
"github.com/cortezaproject/corteza/server/pkg/cli"
"github.com/cortezaproject/corteza/server/pkg/dal"
"github.com/cortezaproject/corteza/server/pkg/envoyx"
"github.com/cortezaproject/corteza/server/pkg/id"
"github.com/cortezaproject/corteza/server/store"
"github.com/cortezaproject/corteza/server/system/service"
sysTypes "github.com/cortezaproject/corteza/server/system/types"
_ "github.com/joho/godotenv/autoload"
"go.uber.org/zap"
)
var (
defaultEnvoy *envoyx.Service
defaultStore store.Storer
defaultDal dal.FullService
)
func init() {
helpers.RecursiveDotEnvLoad()
id.Init(cli.Context())
}
func TestMain(m *testing.M) {
InitTestApp()
os.Exit(m.Run())
}
func InitTestApp() {
ctx := cli.Context()
if defaultStore == nil {
initStore(ctx)
}
if defaultDal == nil {
initDalSvc(ctx)
}
if defaultEnvoy == nil {
initSvc(ctx)
}
}
func initStore(ctx context.Context) {
var err error
dsn := "sqlite3+debug://file::memory:?cache=shared&mode=memory"
defaultStore, err = store.Connect(ctx, zap.NewNop(), dsn, true)
if err != nil {
panic(err)
}
err = store.Upgrade(ctx, zap.NewNop(), defaultStore)
if err != nil {
panic(err)
}
}
func initDalSvc(ctx context.Context) {
var err error
defaultDal, err = dal.New(zap.NewNop(), true)
if err != nil {
panic(err)
}
conn := &sysTypes.DalConnection{
ID: id.Next(),
Handle: sysTypes.DalPrimaryConnectionHandle,
Type: sysTypes.DalPrimaryConnectionResourceType,
Meta: sysTypes.ConnectionMeta{
Name: "Primary Database",
},
Config: sysTypes.ConnectionConfig{
DAL: &sysTypes.ConnectionConfigDAL{
ModelIdent: "compose_record",
},
},
CreatedAt: time.Now(),
}
cw, err := service.MakeDalConnection(conn, defaultStore.ToDalConn())
if err != nil {
panic(err)
}
err = defaultDal.ReplaceConnection(ctx, cw, true)
if err != nil {
panic(err)
}
}
func cleanup(t *testing.T) {
var (
ctx = context.Background()
)
err := collect(
store.TruncateComposeCharts(ctx, defaultStore),
store.TruncateComposeModules(ctx, defaultStore),
store.TruncateComposeModuleFields(ctx, defaultStore),
store.TruncateComposeNamespaces(ctx, defaultStore),
store.TruncateComposePages(ctx, defaultStore),
store.TruncateApplications(ctx, defaultStore),
store.TruncateApigwRoutes(ctx, defaultStore),
store.TruncateApigwFilters(ctx, defaultStore),
store.TruncateAuthClients(ctx, defaultStore),
store.TruncateQueues(ctx, defaultStore),
store.TruncateReports(ctx, defaultStore),
store.TruncateRoles(ctx, defaultStore),
store.TruncateTemplates(ctx, defaultStore),
store.TruncateUsers(ctx, defaultStore),
store.TruncateDalConnections(ctx, defaultStore),
store.TruncateDalSensitivityLevels(ctx, defaultStore),
store.TruncateRbacRules(ctx, defaultStore),
store.TruncateResourceTranslations(ctx, defaultStore),
store.TruncateAutomationWorkflows(ctx, defaultStore),
store.TruncateAutomationTriggers(ctx, defaultStore),
truncateRecords(ctx),
)
if err != nil {
t.Fatalf("failed to decode scenario data: %v", err)
}
}
func truncateRecords(ctx context.Context) error {
models, err := defaultDal.SearchModels(ctx)
if err != nil {
return err
}
for _, model := range models {
err = defaultDal.Truncate(ctx, model.ToFilter(), nil)
if err != nil {
return err
}
}
return nil
}
func collect(ee ...error) error {
for _, e := range ee {
if e != nil {
return e
}
}
return nil
}
func initSvc(ctx context.Context) {
defaultEnvoy = envoyx.New()
defaultEnvoy.AddDecoder(envoyx.DecodeTypeURI,
composeEnvoy.YamlDecoder{},
systemEnvoy.YamlDecoder{},
automationEnvoy.YamlDecoder{},
)
defaultEnvoy.AddDecoder(envoyx.DecodeTypeStore,
composeEnvoy.StoreDecoder{},
systemEnvoy.StoreDecoder{},
automationEnvoy.StoreDecoder{},
)
defaultEnvoy.AddEncoder(envoyx.EncodeTypeIo,
composeEnvoy.YamlEncoder{},
systemEnvoy.YamlEncoder{},
automationEnvoy.YamlEncoder{},
)
defaultEnvoy.AddEncoder(envoyx.EncodeTypeStore,
composeEnvoy.StoreEncoder{},
systemEnvoy.StoreEncoder{},
automationEnvoy.StoreEncoder{},
)
defaultEnvoy.AddEncoder(envoyx.EncodeTypeIo,
composeEnvoy.CsvEncoder{},
)
}

View File

@@ -0,0 +1,260 @@
package envoy
import (
"context"
"fmt"
"os"
"testing"
"github.com/cortezaproject/corteza/server/compose/types"
"github.com/cortezaproject/corteza/server/pkg/envoyx"
"github.com/cortezaproject/corteza/server/pkg/rbac"
"github.com/cortezaproject/corteza/server/store"
systemTypes "github.com/cortezaproject/corteza/server/system/types"
"github.com/davecgh/go-spew/spew"
"github.com/stretchr/testify/require"
)
func TestRbacImportExport(t *testing.T) {
var (
ctx = context.Background()
req = require.New(t)
nodes envoyx.NodeSet
gg *envoyx.DepGraph
err error
)
cleanup(t)
// The test
//
// * imports some YAML files
// * checks the DB state
// * exports the DB into a YAML
// * clears the DB
// * imports the YAML
// * checks the DB state the same way as before
//
// The above outlined flow allows us to trivially check if the data is both
// imported and exported correctly.
//
// The initial step could also manually populate the DB but the YAML import
// is more convenient.
t.Run("initial import", func(t *testing.T) {
t.Run("parse configs", func(t *testing.T) {
nodes, _, err = defaultEnvoy.Decode(ctx, envoyx.DecodeParams{
Type: envoyx.DecodeTypeURI,
Params: map[string]any{
"uri": "file://testdata/rbac",
},
})
req.NoError(err)
})
t.Run("bake", func(t *testing.T) {
gg, err = defaultEnvoy.Bake(ctx, envoyx.EncodeParams{
Type: envoyx.EncodeTypeStore,
Params: map[string]any{
"storer": defaultStore,
"dal": defaultDal,
},
}, nil, nodes...)
req.NoError(err)
})
t.Run("import into DB", func(t *testing.T) {
err = defaultEnvoy.Encode(ctx, envoyx.EncodeParams{
Type: envoyx.EncodeTypeStore,
Params: map[string]any{
"storer": defaultStore,
"dal": defaultDal,
},
}, gg)
req.NoError(err)
})
assertRBACState(ctx, t, defaultStore, req)
})
// Prepare a temp file where we'll dump the YAML into
auxFile, err := os.CreateTemp(os.TempDir(), "*.yaml")
req.NoError(err)
spew.Dump(auxFile.Name())
// defer os.Remove(auxFile.Name())
defer auxFile.Close()
var rules envoyx.NodeSet
t.Run("export", func(t *testing.T) {
t.Run("export from DB", func(t *testing.T) {
nodes, _, err = defaultEnvoy.Decode(ctx, envoyx.DecodeParams{
Type: envoyx.DecodeTypeStore,
Params: map[string]any{
"storer": defaultStore,
"dal": defaultDal,
},
Filter: map[string]envoyx.ResourceFilter{
types.ModuleResourceType: {},
types.NamespaceResourceType: {},
systemTypes.RoleResourceType: {},
},
})
req.NoError(err)
})
var rr rbac.RuleSet
t.Run("get all rules", func(t *testing.T) {
rr, _, err = store.SearchRbacRules(ctx, defaultStore, rbac.RuleFilter{})
req.NoError(err)
})
t.Run("connect rules to resources", func(t *testing.T) {
rules, err = envoyx.RBACRulesForNodes(rr, nodes...)
req.NoError(err)
nodes = append(nodes, rules...)
})
t.Run("bake", func(t *testing.T) {
gg, err = defaultEnvoy.Bake(ctx, envoyx.EncodeParams{
Type: envoyx.EncodeTypeStore,
Params: map[string]any{
"storer": defaultStore,
"dal": defaultDal,
},
}, nil, nodes...)
req.NoError(err)
})
t.Run("write file", func(t *testing.T) {
err = defaultEnvoy.Encode(ctx, envoyx.EncodeParams{
Type: envoyx.EncodeTypeIo,
Params: map[string]any{
"writer": auxFile,
},
}, gg)
req.NoError(err)
})
})
cleanup(t)
t.Run("second import", func(t *testing.T) {
t.Run("yaml parse", func(t *testing.T) {
nodes, _, err = defaultEnvoy.Decode(ctx, envoyx.DecodeParams{
Type: envoyx.DecodeTypeURI,
Params: map[string]any{
"uri": fmt.Sprintf("file://%s", auxFile.Name()),
},
})
req.NoError(err)
})
t.Run("bake", func(t *testing.T) {
gg, err = defaultEnvoy.Bake(ctx, envoyx.EncodeParams{
Type: envoyx.EncodeTypeStore,
Params: map[string]any{
"storer": defaultStore,
"dal": defaultDal,
},
}, nil, nodes...)
req.NoError(err)
})
t.Run("run import", func(t *testing.T) {
err = defaultEnvoy.Encode(ctx, envoyx.EncodeParams{
Type: envoyx.EncodeTypeStore,
Params: map[string]any{
"storer": defaultStore,
"dal": defaultDal,
},
}, gg)
req.NoError(err)
})
assertRBACState(ctx, t, defaultStore, req)
})
}
func assertRBACState(ctx context.Context, t *testing.T, s store.Storer, req *require.Assertions) {
t.Run("check state", func(t *testing.T) {
namespaces, _, err := store.SearchComposeNamespaces(ctx, defaultStore, types.NamespaceFilter{})
req.NoError(err)
ns1 := namespaces[0]
modules, _, err := store.SearchComposeModules(ctx, defaultStore, types.ModuleFilter{})
req.NoError(err)
mod1 := modules[0]
roles, _, err := store.SearchRoles(ctx, defaultStore, systemTypes.RoleFilter{})
req.NoError(err)
role1 := roles.FindByHandle("test_role_1")
role2 := roles.FindByHandle("test_role_2")
rr, _, err := store.SearchRbacRules(ctx, defaultStore, rbac.RuleFilter{})
req.NoError(err)
allows := rr.FilterAccess(rbac.Allow)
compareRules(req, *allows.FilterOperation("op2")[0], rbac.Rule{
RoleID: role1.ID,
Resource: fmt.Sprintf("corteza::compose:namespace/%d", ns1.ID),
Operation: "op2",
Access: rbac.Allow,
})
compareRules(req, *allows.FilterOperation("op3")[0], rbac.Rule{
RoleID: role1.ID,
Resource: fmt.Sprintf("corteza::compose:module/%d/*", ns1.ID),
Operation: "op3",
Access: rbac.Allow,
})
compareRules(req, *allows.FilterOperation("op4")[0], rbac.Rule{
RoleID: role1.ID,
Resource: fmt.Sprintf("corteza::compose:module/%d/%d", ns1.ID, mod1.ID),
Operation: "op4",
Access: rbac.Allow,
})
denies := rr.FilterAccess(rbac.Deny)
compareRules(req, *denies.FilterOperation("op2")[0], rbac.Rule{
RoleID: role2.ID,
Resource: fmt.Sprintf("corteza::compose:namespace/%d", ns1.ID),
Operation: "op2",
Access: rbac.Deny,
})
compareRules(req, *denies.FilterOperation("op3")[0], rbac.Rule{
RoleID: role2.ID,
Resource: fmt.Sprintf("corteza::compose:module/%d/*", ns1.ID),
Operation: "op3",
Access: rbac.Deny,
})
compareRules(req, *denies.FilterOperation("op4")[0], rbac.Rule{
RoleID: role2.ID,
Resource: fmt.Sprintf("corteza::compose:module/%d/%d", ns1.ID, mod1.ID),
Operation: "op4",
Access: rbac.Deny,
})
})
}
func compareRules(req *require.Assertions, a, b rbac.Rule) {
if a.RoleID != b.RoleID {
req.FailNow("RoleID missmatch")
}
if a.Resource != b.Resource {
req.FailNow("Resource missmatch")
}
if a.Operation != b.Operation {
req.FailNow("Operation missmatch")
}
if a.Access != b.Access {
req.FailNow("Access missmatch")
}
}

View File

@@ -0,0 +1,374 @@
package envoy
import (
"context"
"fmt"
"os"
"strconv"
"testing"
"github.com/cortezaproject/corteza/server/compose/dalutils"
"github.com/cortezaproject/corteza/server/compose/envoy"
"github.com/cortezaproject/corteza/server/compose/types"
"github.com/cortezaproject/corteza/server/pkg/dal"
"github.com/cortezaproject/corteza/server/pkg/envoyx"
"github.com/cortezaproject/corteza/server/store"
systemTypes "github.com/cortezaproject/corteza/server/system/types"
"github.com/davecgh/go-spew/spew"
"github.com/stretchr/testify/require"
)
func TestRecordsImportExport(t *testing.T) {
var (
ctx = context.Background()
req = require.New(t)
nodes envoyx.NodeSet
providers []envoyx.Provider
gg *envoyx.DepGraph
err error
)
_ = gg
cleanup(t)
// The test
//
// * imports some YAML files
// * checks the DB state
// * exports the DB into a YAML
// * clears the DB
// * imports the YAML
// * checks the DB state the same way as before
//
// The above outlined flow allows us to trivially check if the data is both
// imported and exported correctly.
//
// The initial step could also manually populate the DB but the YAML import
// is more convenient.
t.Run("initial import", func(t *testing.T) {
t.Run("parse configs", func(t *testing.T) {
nodes, providers, err = defaultEnvoy.Decode(ctx, envoyx.DecodeParams{
Type: envoyx.DecodeTypeURI,
Params: map[string]any{
"uri": "file://testdata/datasource_records",
},
})
req.NoError(err)
})
t.Run("bake", func(t *testing.T) {
gg, err = defaultEnvoy.Bake(ctx, envoyx.EncodeParams{
Type: envoyx.EncodeTypeStore,
Params: map[string]any{
"storer": defaultStore,
"dal": defaultDal,
},
}, providers, nodes...)
req.NoError(err)
})
t.Run("import into DB", func(t *testing.T) {
err = defaultEnvoy.Encode(ctx, envoyx.EncodeParams{
Type: envoyx.EncodeTypeStore,
Params: map[string]any{
"storer": defaultStore,
"dal": defaultDal,
},
}, gg)
req.NoError(err)
})
assertRecordState(ctx, t, defaultStore, defaultDal, req)
})
// Prepare a temp file where we'll dump the YAML into
auxFile, err := os.CreateTemp(os.TempDir(), "*.csv")
req.NoError(err)
spew.Dump(auxFile.Name())
// defer os.Remove(auxFile.Name())
defer auxFile.Close()
t.Run("export", func(t *testing.T) {
t.Run("export from DB", func(t *testing.T) {
nodes, _, err = defaultEnvoy.Decode(ctx, envoyx.DecodeParams{
Type: envoyx.DecodeTypeStore,
Params: map[string]any{
"storer": defaultStore,
"dal": defaultDal,
},
Filter: map[string]envoyx.ResourceFilter{
envoy.ComposeRecordDatasourceAuxType: {
Refs: map[string]envoyx.Ref{
"NamespaceID": {
ResourceType: types.NamespaceResourceType,
Identifiers: envoyx.MakeIdentifiers("test_ns_1"),
Scope: envoyx.Scope{
ResourceType: types.NamespaceResourceType,
Identifiers: envoyx.MakeIdentifiers("test_ns_1")},
},
"ModuleID": {
ResourceType: types.ModuleResourceType,
Identifiers: envoyx.MakeIdentifiers("test_ns_1_mod_1"),
Scope: envoyx.Scope{
ResourceType: types.NamespaceResourceType,
Identifiers: envoyx.MakeIdentifiers("test_ns_1"),
},
},
},
Scope: envoyx.Scope{
ResourceType: types.NamespaceResourceType,
Identifiers: envoyx.MakeIdentifiers("test_ns_1"),
},
},
},
})
req.NoError(err)
})
t.Run("bake", func(t *testing.T) {
gg, err = defaultEnvoy.Bake(ctx, envoyx.EncodeParams{
Type: envoyx.EncodeTypeStore,
Params: map[string]any{
"storer": defaultStore,
"dal": defaultDal,
},
}, nil, nodes...)
req.NoError(err)
})
t.Run("write file", func(t *testing.T) {
err = defaultEnvoy.Encode(ctx, envoyx.EncodeParams{
Type: envoyx.EncodeTypeIo,
Params: map[string]any{
"writer": auxFile,
},
}, gg)
req.NoError(err)
})
})
cleanup(t)
t.Run("second import", func(t *testing.T) {
t.Run("yaml parse", func(t *testing.T) {
nodes, _, err = defaultEnvoy.Decode(ctx, envoyx.DecodeParams{
Type: envoyx.DecodeTypeURI,
Params: map[string]any{
"uri": "file://testdata/datasource_records",
},
})
req.NoError(err)
_, providers, err = defaultEnvoy.Decode(ctx, envoyx.DecodeParams{
Type: envoyx.DecodeTypeURI,
Params: map[string]any{
"uri": fmt.Sprintf("file://%s", auxFile.Name()),
},
})
req.NoError(err)
for _, p := range providers {
p.SetIdent("records.csv")
}
})
t.Run("bake", func(t *testing.T) {
gg, err = defaultEnvoy.Bake(ctx, envoyx.EncodeParams{
Type: envoyx.EncodeTypeStore,
Params: map[string]any{
"storer": defaultStore,
"dal": defaultDal,
},
}, providers, nodes...)
req.NoError(err)
})
t.Run("run import", func(t *testing.T) {
err = defaultEnvoy.Encode(ctx, envoyx.EncodeParams{
Type: envoyx.EncodeTypeStore,
Params: map[string]any{
"storer": defaultStore,
"dal": defaultDal,
},
}, gg)
req.NoError(err)
})
assertRecordState(ctx, t, defaultStore, defaultDal, req)
})
}
func TestRecordsXrefs(t *testing.T) {
var (
ctx = context.Background()
req = require.New(t)
nodes envoyx.NodeSet
providers []envoyx.Provider
gg *envoyx.DepGraph
err error
)
_ = gg
cleanup(t)
t.Run("parse configs", func(t *testing.T) {
nodes, providers, err = defaultEnvoy.Decode(ctx, envoyx.DecodeParams{
Type: envoyx.DecodeTypeURI,
Params: map[string]any{
"uri": "file://testdata/records/cross_ref",
},
})
req.NoError(err)
})
t.Run("bake", func(t *testing.T) {
gg, err = defaultEnvoy.Bake(ctx, envoyx.EncodeParams{
Type: envoyx.EncodeTypeStore,
Params: map[string]any{
"storer": defaultStore,
"dal": defaultDal,
},
}, providers, nodes...)
req.NoError(err)
})
t.Run("import into DB", func(t *testing.T) {
err = defaultEnvoy.Encode(ctx, envoyx.EncodeParams{
Type: envoyx.EncodeTypeStore,
Params: map[string]any{
"storer": defaultStore,
"dal": defaultDal,
},
}, gg)
req.NoError(err)
})
ns, err := store.LookupComposeNamespaceBySlug(ctx, defaultStore, "test_ns")
req.NoError(err)
modParent, err := store.LookupComposeModuleByNamespaceIDHandle(ctx, defaultStore, ns.ID, "mod_parent")
req.NoError(err)
modChild, err := store.LookupComposeModuleByNamespaceIDHandle(ctx, defaultStore, ns.ID, "mod_child")
req.NoError(err)
parentRecords, _, err := dalutils.ComposeRecordsList(ctx, defaultDal, modParent, types.RecordFilter{})
req.NoError(err)
compareValues(req, parentRecords[0].Values, types.RecordValueSet{{Name: "name", Value: "Jane Doe"}, {Name: "code", Value: "3124"}})
compareValues(req, parentRecords[1].Values, types.RecordValueSet{{Name: "name", Value: "Ana Banana"}, {Name: "code", Value: "4153"}})
compareValues(req, parentRecords[2].Values, types.RecordValueSet{{Name: "name", Value: "Qwerty"}, {Name: "code", Value: "83172"}})
childRecords, _, err := dalutils.ComposeRecordsList(ctx, defaultDal, modChild, types.RecordFilter{})
req.NoError(err)
compareValues(req, childRecords[0].Values, types.RecordValueSet{{Name: "user_name", Value: "Daniel Adams", Ref: 0}, {Name: "parent", Value: strconv.FormatUint(parentRecords[0].ID, 10), Ref: parentRecords[0].ID}, {Name: "alt", Value: strconv.FormatUint(childRecords[2].ID, 10), Ref: childRecords[2].ID}})
compareValues(req, childRecords[1].Values, types.RecordValueSet{{Name: "user_name", Value: "Jane Alex", Ref: 0}, {Name: "parent", Value: strconv.FormatUint(parentRecords[1].ID, 10), Ref: parentRecords[1].ID}, {Name: "alt", Value: strconv.FormatUint(childRecords[0].ID, 10), Ref: childRecords[0].ID}})
compareValues(req, childRecords[2].Values, types.RecordValueSet{{Name: "user_name", Value: "Fish", Ref: 0}, {Name: "parent", Value: strconv.FormatUint(parentRecords[2].ID, 10), Ref: parentRecords[2].ID}, {Name: "alt", Value: strconv.FormatUint(childRecords[1].ID, 10), Ref: childRecords[1].ID}})
}
func TestRecordsUserRefs(t *testing.T) {
var (
ctx = context.Background()
req = require.New(t)
nodes envoyx.NodeSet
providers []envoyx.Provider
gg *envoyx.DepGraph
err error
)
_ = gg
cleanup(t)
t.Run("parse configs", func(t *testing.T) {
nodes, providers, err = defaultEnvoy.Decode(ctx, envoyx.DecodeParams{
Type: envoyx.DecodeTypeURI,
Params: map[string]any{
"uri": "file://testdata/records/user_ref",
},
})
req.NoError(err)
})
t.Run("bake", func(t *testing.T) {
gg, err = defaultEnvoy.Bake(ctx, envoyx.EncodeParams{
Type: envoyx.EncodeTypeStore,
Params: map[string]any{
"storer": defaultStore,
"dal": defaultDal,
},
}, providers, nodes...)
req.NoError(err)
})
t.Run("import into DB", func(t *testing.T) {
err = defaultEnvoy.Encode(ctx, envoyx.EncodeParams{
Type: envoyx.EncodeTypeStore,
Params: map[string]any{
"storer": defaultStore,
"dal": defaultDal,
},
}, gg)
req.NoError(err)
})
ns, err := store.LookupComposeNamespaceBySlug(ctx, defaultStore, "test_ns")
req.NoError(err)
mod, err := store.LookupComposeModuleByNamespaceIDHandle(ctx, defaultStore, ns.ID, "test_mod")
req.NoError(err)
rr, _, err := dalutils.ComposeRecordsList(ctx, defaultDal, mod, types.RecordFilter{})
req.NoError(err)
uu, _, err := store.SearchUsers(ctx, defaultStore, systemTypes.UserFilter{})
req.NoError(err)
usr0 := uu[0]
usr1 := uu[1]
usr2 := uu[2]
usrx := uu[3]
compareValues(req, rr[0].Values, types.RecordValueSet{{Name: "name", Value: "Jane Doe"}, {Name: "code", Value: "3124"}, {Name: "rel_user", Value: strconv.FormatUint(usr0.ID, 10), Ref: usr0.ID}})
compareValues(req, rr[1].Values, types.RecordValueSet{{Name: "name", Value: "Ana Banana"}, {Name: "code", Value: "4153"}, {Name: "rel_user", Value: strconv.FormatUint(usr1.ID, 10), Ref: usr1.ID}})
compareValues(req, rr[2].Values, types.RecordValueSet{{Name: "name", Value: "Qwerty"}, {Name: "code", Value: "83172"}, {Name: "rel_user", Value: strconv.FormatUint(usr2.ID, 10), Ref: usr2.ID}})
req.Equal(usrx.ID, rr[0].CreatedBy)
req.Equal(usrx.ID, rr[1].CreatedBy)
req.Equal(usrx.ID, rr[2].CreatedBy)
}
func assertRecordState(ctx context.Context, t *testing.T, s store.Storer, dl dal.FullService, req *require.Assertions) {
t.Run("check state", func(t *testing.T) {
ns, err := store.LookupComposeNamespaceBySlug(ctx, defaultStore, "test_ns_1")
req.NoError(err)
mod, err := store.LookupComposeModuleByNamespaceIDHandle(ctx, defaultStore, ns.ID, "test_ns_1_mod_1")
req.NoError(err)
rr, _, err := dalutils.ComposeRecordsList(ctx, dl, mod, types.RecordFilter{})
req.NoError(err)
req.Len(rr, 8)
compareValues(req, rr[0].Values, types.RecordValueSet{{Name: "test_ns_1_mod_1_f1", Value: "row_1_src_c1"}, {Name: "test_ns_1_mod_1_f2", Value: "row_1_src_c2"}})
compareValues(req, rr[1].Values, types.RecordValueSet{{Name: "test_ns_1_mod_1_f1", Value: "row_2_src_c1"}, {Name: "test_ns_1_mod_1_f2", Value: "row_2_src_c2"}})
compareValues(req, rr[2].Values, types.RecordValueSet{{Name: "test_ns_1_mod_1_f1", Value: "row_3_src_c1"}, {Name: "test_ns_1_mod_1_f2", Value: "row_3_src_c2"}})
compareValues(req, rr[3].Values, types.RecordValueSet{{Name: "test_ns_1_mod_1_f1", Value: "row_4_src_c1"}, {Name: "test_ns_1_mod_1_f2", Value: "row_4_src_c2"}})
compareValues(req, rr[4].Values, types.RecordValueSet{{Name: "test_ns_1_mod_1_f1", Value: "row_5_src_c1"}, {Name: "test_ns_1_mod_1_f2", Value: "row_5_src_c2"}})
compareValues(req, rr[5].Values, types.RecordValueSet{{Name: "test_ns_1_mod_1_f1", Value: "row_6_src_c1"}, {Name: "test_ns_1_mod_1_f2", Value: "row_6_src_c2"}})
compareValues(req, rr[6].Values, types.RecordValueSet{{Name: "test_ns_1_mod_1_f1", Value: "row_7_src_c1"}, {Name: "test_ns_1_mod_1_f2", Value: "row_7_src_c2"}})
compareValues(req, rr[7].Values, types.RecordValueSet{{Name: "test_ns_1_mod_1_f1", Value: "row_8_src_c1"}, {Name: "test_ns_1_mod_1_f2", Value: "row_8_src_c2"}})
})
}
func compareValues(req *require.Assertions, a, b types.RecordValueSet) {
for _, va := range a {
vb := b.Get(va.Name, va.Place)
req.NotNil(vb)
req.Equal(va.Value, vb.Value)
}
}

View File

@@ -0,0 +1,80 @@
package envoy
import (
"context"
"fmt"
"testing"
"github.com/cortezaproject/corteza/server/compose/types"
"github.com/cortezaproject/corteza/server/pkg/envoyx"
"github.com/cortezaproject/corteza/server/store"
systemTypes "github.com/cortezaproject/corteza/server/system/types"
"github.com/stretchr/testify/require"
"golang.org/x/text/language"
)
// Importing configs where the same (lang, resource, key) translation appears
// multiple times (across files or on repeated imports) must not trip the
// unique constraint on resource translations
func TestResTrImportDuplicates(t *testing.T) {
var (
ctx = context.Background()
req = require.New(t)
)
cleanup(t)
runImport := func(uri string) error {
nodes, _, err := defaultEnvoy.Decode(ctx, envoyx.DecodeParams{
Type: envoyx.DecodeTypeURI,
Params: map[string]any{
"uri": uri,
},
})
if err != nil {
return err
}
gg, err := defaultEnvoy.Bake(ctx, envoyx.EncodeParams{
Type: envoyx.EncodeTypeStore,
Params: map[string]any{
"storer": defaultStore,
"dal": defaultDal,
},
}, nil, nodes...)
if err != nil {
return err
}
return defaultEnvoy.Encode(ctx, envoyx.EncodeParams{
Type: envoyx.EncodeTypeStore,
Params: map[string]any{
"storer": defaultStore,
"dal": defaultDal,
},
}, gg)
}
t.Run("import with duplicated translations", func(t *testing.T) {
req.NoError(runImport("file://testdata/locale_dup"))
namespaces, _, err := store.SearchComposeNamespaces(ctx, defaultStore, types.NamespaceFilter{})
req.NoError(err)
req.Len(namespaces, 1)
nsRes := fmt.Sprintf("corteza::compose:namespace/%d", namespaces[0].ID)
ll, _, err := store.SearchResourceTranslations(ctx, defaultStore, systemTypes.ResourceTranslationFilter{})
req.NoError(err)
// Only one row per (lang, resource, key) may exist; the later
// duplicate wins and may not be silently dropped
en := ll.FilterLanguage(language.English)
req.Len(en.FilterResource(nsRes).FilterKey("res_tr_1"), 1)
req.Equal("res_tr_1_text_duplicate", en.FilterResource(nsRes).FilterKey("res_tr_1")[0].Message)
req.Len(en.FilterResource(nsRes).FilterKey("res_tr_2"), 1)
})
t.Run("re-import over existing translations", func(t *testing.T) {
req.NoError(runImport("file://testdata/locale_dup"))
})
}

View File

@@ -0,0 +1,243 @@
package envoy
import (
"context"
"fmt"
"os"
"testing"
"github.com/cortezaproject/corteza/server/compose/types"
"github.com/cortezaproject/corteza/server/pkg/envoyx"
"github.com/cortezaproject/corteza/server/store"
systemTypes "github.com/cortezaproject/corteza/server/system/types"
"github.com/davecgh/go-spew/spew"
"github.com/stretchr/testify/require"
"golang.org/x/text/language"
)
func TestResTrImportExport(t *testing.T) {
var (
ctx = context.Background()
req = require.New(t)
nodes envoyx.NodeSet
gg *envoyx.DepGraph
err error
)
cleanup(t)
// The test
//
// * imports some YAML files
// * checks the DB state
// * exports the DB into a YAML
// * clears the DB
// * imports the YAML
// * checks the DB state the same way as before
//
// The above outlined flow allows us to trivially check if the data is both
// imported and exported correctly.
//
// The initial step could also manually populate the DB but the YAML import
// is more convenient.
t.Run("initial import", func(t *testing.T) {
t.Run("parse configs", func(t *testing.T) {
nodes, _, err = defaultEnvoy.Decode(ctx, envoyx.DecodeParams{
Type: envoyx.DecodeTypeURI,
Params: map[string]any{
"uri": "file://testdata/locale",
},
})
req.NoError(err)
})
t.Run("bake", func(t *testing.T) {
gg, err = defaultEnvoy.Bake(ctx, envoyx.EncodeParams{
Type: envoyx.EncodeTypeStore,
Params: map[string]any{
"storer": defaultStore,
"dal": defaultDal,
},
}, nil, nodes...)
req.NoError(err)
})
t.Run("import into DB", func(t *testing.T) {
err = defaultEnvoy.Encode(ctx, envoyx.EncodeParams{
Type: envoyx.EncodeTypeStore,
Params: map[string]any{
"storer": defaultStore,
"dal": defaultDal,
},
}, gg)
req.NoError(err)
})
req.NoError(err)
assertLocaleState(ctx, t, defaultStore, req)
})
// Prepare a temp file where we'll dump the YAML into
auxFile, err := os.CreateTemp(os.TempDir(), "*.yaml")
req.NoError(err)
spew.Dump(auxFile.Name())
// defer os.Remove(auxFile.Name())
defer auxFile.Close()
var translations envoyx.NodeSet
t.Run("export", func(t *testing.T) {
t.Run("export from DB", func(t *testing.T) {
nodes, _, err = defaultEnvoy.Decode(ctx, envoyx.DecodeParams{
Type: envoyx.DecodeTypeStore,
Params: map[string]any{
"storer": defaultStore,
"dal": defaultDal,
},
Filter: map[string]envoyx.ResourceFilter{
types.ModuleResourceType: {},
types.NamespaceResourceType: {},
systemTypes.RoleResourceType: {},
},
})
req.NoError(err)
})
var tt systemTypes.ResourceTranslationSet
t.Run("get all rules", func(t *testing.T) {
tt, _, err = store.SearchResourceTranslations(ctx, defaultStore, systemTypes.ResourceTranslationFilter{})
req.NoError(err)
})
t.Run("connect rules to resources", func(t *testing.T) {
translations, err = envoyx.ResourceTranslationsForNodes(tt, nodes...)
req.NoError(err)
nodes = append(nodes, translations...)
})
t.Run("bake", func(t *testing.T) {
gg, err = defaultEnvoy.Bake(ctx, envoyx.EncodeParams{
Type: envoyx.EncodeTypeStore,
Params: map[string]any{
"storer": defaultStore,
"dal": defaultDal,
},
}, nil, nodes...)
req.NoError(err)
})
t.Run("write file", func(t *testing.T) {
err = defaultEnvoy.Encode(ctx, envoyx.EncodeParams{
Type: envoyx.EncodeTypeIo,
Params: map[string]any{
"writer": auxFile,
},
}, gg)
req.NoError(err)
})
})
cleanup(t)
t.Run("second import", func(t *testing.T) {
t.Run("yaml parse", func(t *testing.T) {
nodes, _, err = defaultEnvoy.Decode(ctx, envoyx.DecodeParams{
Type: envoyx.DecodeTypeURI,
Params: map[string]any{
"uri": fmt.Sprintf("file://%s", auxFile.Name()),
},
})
req.NoError(err)
})
t.Run("bake", func(t *testing.T) {
gg, err = defaultEnvoy.Bake(ctx, envoyx.EncodeParams{
Type: envoyx.EncodeTypeStore,
Params: map[string]any{
"storer": defaultStore,
"dal": defaultDal,
},
}, nil, nodes...)
req.NoError(err)
})
t.Run("run import", func(t *testing.T) {
err = defaultEnvoy.Encode(ctx, envoyx.EncodeParams{
Type: envoyx.EncodeTypeStore,
Params: map[string]any{
"storer": defaultStore,
"dal": defaultDal,
},
}, gg)
req.NoError(err)
})
assertLocaleState(ctx, t, defaultStore, req)
})
}
func assertLocaleState(ctx context.Context, t *testing.T, s store.Storer, req *require.Assertions) {
t.Run("check state", func(t *testing.T) {
namespaces, _, err := store.SearchComposeNamespaces(ctx, defaultStore, types.NamespaceFilter{})
req.NoError(err)
ns1 := namespaces[0]
modules, _, err := store.SearchComposeModules(ctx, defaultStore, types.ModuleFilter{})
req.NoError(err)
mod1 := modules[0]
ll, _, err := store.SearchResourceTranslations(ctx, defaultStore, systemTypes.ResourceTranslationFilter{})
req.NoError(err)
en := ll.FilterLanguage(language.English)
compareResourceTranslations(req, *en.FilterKey("res_tr_1")[0], systemTypes.ResourceTranslation{
Resource: fmt.Sprintf("corteza::compose:namespace/%d", ns1.ID),
K: "res_tr_1",
Message: "res_tr_1_text",
})
compareResourceTranslations(req, *en.FilterKey("res_tr_2")[0], systemTypes.ResourceTranslation{
Resource: fmt.Sprintf("corteza::compose:namespace/%d", ns1.ID),
K: "res_tr_2",
Message: "res_tr_2_text",
})
compareResourceTranslations(req, *en.FilterKey("res_tr_3")[0], systemTypes.ResourceTranslation{
Resource: fmt.Sprintf("corteza::compose:module/%d/%d", ns1.ID, mod1.ID),
K: "res_tr_3",
Message: "res_tr_3_text",
})
de := ll.FilterLanguage(language.English)
compareResourceTranslations(req, *de.FilterKey("res_tr_1")[0], systemTypes.ResourceTranslation{
Resource: fmt.Sprintf("corteza::compose:namespace/%d", ns1.ID),
K: "res_tr_1",
Message: "res_tr_1_text",
})
compareResourceTranslations(req, *de.FilterKey("res_tr_2")[0], systemTypes.ResourceTranslation{
Resource: fmt.Sprintf("corteza::compose:namespace/%d", ns1.ID),
K: "res_tr_2",
Message: "res_tr_2_text",
})
compareResourceTranslations(req, *de.FilterKey("res_tr_3")[0], systemTypes.ResourceTranslation{
Resource: fmt.Sprintf("corteza::compose:module/%d/%d", ns1.ID, mod1.ID),
K: "res_tr_3",
Message: "res_tr_3_text",
})
})
}
func compareResourceTranslations(req *require.Assertions, a, b systemTypes.ResourceTranslation) {
if a.Resource != b.Resource {
req.FailNow("Resource missmatch")
}
if a.K != b.K {
req.FailNow("K missmatch")
}
if a.Message != b.Message {
req.FailNow("Message missmatch")
}
}

View File

@@ -0,0 +1,192 @@
package envoy
import (
"context"
"fmt"
"os"
"testing"
"github.com/cortezaproject/corteza/server/pkg/envoyx"
"github.com/cortezaproject/corteza/server/pkg/rbac"
"github.com/cortezaproject/corteza/server/store"
"github.com/cortezaproject/corteza/server/system/types"
"github.com/davecgh/go-spew/spew"
"github.com/stretchr/testify/require"
)
func TestSettingImportExport(t *testing.T) {
var (
ctx = context.Background()
req = require.New(t)
nodes envoyx.NodeSet
gg *envoyx.DepGraph
err error
)
cleanup(t)
// The test
//
// * imports some YAML files
// * checks the DB state
// * exports the DB into a YAML
// * clears the DB
// * imports the YAML
// * checks the DB state the same way as before
//
// The above outlined flow allows us to trivially check if the data is both
// imported and exported correctly.
//
// The initial step could also manually populate the DB but the YAML import
// is more convenient.
t.Run("initial import", func(t *testing.T) {
t.Run("parse configs", func(t *testing.T) {
nodes, _, err = defaultEnvoy.Decode(ctx, envoyx.DecodeParams{
Type: envoyx.DecodeTypeURI,
Params: map[string]any{
"uri": "file://testdata/settings",
},
})
req.NoError(err)
})
t.Run("bake", func(t *testing.T) {
gg, err = defaultEnvoy.Bake(ctx, envoyx.EncodeParams{
Type: envoyx.EncodeTypeStore,
Params: map[string]any{
"storer": defaultStore,
"dal": defaultDal,
},
}, nil, nodes...)
req.NoError(err)
})
t.Run("import into DB", func(t *testing.T) {
err = defaultEnvoy.Encode(ctx, envoyx.EncodeParams{
Type: envoyx.EncodeTypeStore,
Params: map[string]any{
"storer": defaultStore,
"dal": defaultDal,
},
}, gg)
req.NoError(err)
})
assertSettingState(ctx, t, defaultStore, req)
})
// Prepare a temp file where we'll dump the YAML into
auxFile, err := os.CreateTemp(os.TempDir(), "*.yaml")
req.NoError(err)
spew.Dump(auxFile.Name())
// defer os.Remove(auxFile.Name())
defer auxFile.Close()
var rules envoyx.NodeSet
t.Run("export", func(t *testing.T) {
t.Run("export from DB", func(t *testing.T) {
nodes, _, err = defaultEnvoy.Decode(ctx, envoyx.DecodeParams{
Type: envoyx.DecodeTypeStore,
Params: map[string]any{
"storer": defaultStore,
"dal": defaultDal,
},
Filter: map[string]envoyx.ResourceFilter{
types.SettingValueResourceType: {},
},
})
req.NoError(err)
})
var rr rbac.RuleSet
t.Run("get all rules", func(t *testing.T) {
rr, _, err = store.SearchRbacRules(ctx, defaultStore, rbac.RuleFilter{})
req.NoError(err)
})
t.Run("connect rules to resources", func(t *testing.T) {
rules, err = envoyx.RBACRulesForNodes(rr, nodes...)
req.NoError(err)
nodes = append(nodes, rules...)
})
t.Run("bake", func(t *testing.T) {
gg, err = defaultEnvoy.Bake(ctx, envoyx.EncodeParams{
Type: envoyx.EncodeTypeStore,
Params: map[string]any{
"storer": defaultStore,
"dal": defaultDal,
},
}, nil, nodes...)
req.NoError(err)
})
t.Run("write file", func(t *testing.T) {
err = defaultEnvoy.Encode(ctx, envoyx.EncodeParams{
Type: envoyx.EncodeTypeIo,
Params: map[string]any{
"writer": auxFile,
},
}, gg)
req.NoError(err)
})
})
cleanup(t)
t.Run("second import", func(t *testing.T) {
t.Run("yaml parse", func(t *testing.T) {
nodes, _, err = defaultEnvoy.Decode(ctx, envoyx.DecodeParams{
Type: envoyx.DecodeTypeURI,
Params: map[string]any{
"uri": fmt.Sprintf("file://%s", auxFile.Name()),
},
})
req.NoError(err)
})
t.Run("bake", func(t *testing.T) {
gg, err = defaultEnvoy.Bake(ctx, envoyx.EncodeParams{
Type: envoyx.EncodeTypeStore,
Params: map[string]any{
"storer": defaultStore,
"dal": defaultDal,
},
}, nil, nodes...)
req.NoError(err)
})
t.Run("run import", func(t *testing.T) {
err = defaultEnvoy.Encode(ctx, envoyx.EncodeParams{
Type: envoyx.EncodeTypeStore,
Params: map[string]any{
"storer": defaultStore,
"dal": defaultDal,
},
}, gg)
req.NoError(err)
})
assertSettingState(ctx, t, defaultStore, req)
})
}
func assertSettingState(ctx context.Context, t *testing.T, s store.Storer, req *require.Assertions) {
t.Run("check state", func(t *testing.T) {
ss, _, err := store.SearchSettingValues(ctx, s, types.SettingsFilter{})
req.NoError(err)
var st *types.SettingValue
st = ss.FindByName("auth.external.enabled")
req.NotNil(st)
st = ss.FindByName("auth.mail.from-address")
req.NotNil(st)
st = ss.FindByName("auth.internal.enabled")
req.NotNil(st)
})
}

View File

@@ -0,0 +1,22 @@
namespace:
test_ns_1:
name: Test Namespace 1
modules:
test_ns_1_mod_1:
name: Test Namespace 1 Module 1
fields:
test_ns_1_mod_1_f1:
title: Test Namespace 1 Module 1 Field 1
test_ns_1_mod_1_f2:
title: Test Namespace 1 Module 1 Field 2
kind: String
source:
- from: records.csv
key: ID
map:
- column: ID
skip: true
- column: test_ns_1_mod_1_f1
field: test_ns_1_mod_1_f1
- column: test_ns_1_mod_1_f2
field: test_ns_1_mod_1_f2

View File

@@ -0,0 +1,9 @@
ID,test_ns_1_mod_1_f1,test_ns_1_mod_1_f2
row_1,row_1_src_c1,row_1_src_c2
row_2,row_2_src_c1,row_2_src_c2
row_3,row_3_src_c1,row_3_src_c2
row_4,row_4_src_c1,row_4_src_c2
row_5,row_5_src_c1,row_5_src_c2
row_6,row_6_src_c1,row_6_src_c2
row_7,row_7_src_c1,row_7_src_c2
row_8,row_8_src_c1,row_8_src_c2
1 ID test_ns_1_mod_1_f1 test_ns_1_mod_1_f2
2 row_1 row_1_src_c1 row_1_src_c2
3 row_2 row_2_src_c1 row_2_src_c2
4 row_3 row_3_src_c1 row_3_src_c2
5 row_4 row_4_src_c1 row_4_src_c2
6 row_5 row_5_src_c1 row_5_src_c2
7 row_6 row_6_src_c1 row_6_src_c2
8 row_7 row_7_src_c1 row_7_src_c2
9 row_8 row_8_src_c1 row_8_src_c2

View File

@@ -0,0 +1,197 @@
namespace:
test_ns_1:
name: Test Namespace 1
enabled: true
modules:
test_ns_1_mod_1:
name: Test Namespace 1 Module 1
fields:
test_ns_1_mod_1_f1:
title: Test Namespace 1 Module 1 Field 1
test_ns_1_mod_1_f2:
title: Test Namespace 1 Module 1 Field 2
kind: Number
test_ns_1_mod_2:
name: Test Namespace 1 Module 2
fields:
test_ns_1_mod_2_f1:
title: Test Namespace 1 Module 2 Field 1
kind: Record
options:
module: test_ns_1_mod_1
test_ns_1_mod_2_f2:
title: Test Namespace 1 Module 2 Field 2
kind: Record
options:
module: test_ns_1_mod_2
charts:
test_ns_1_chart_1:
name: Test Namespace 1 Chart 1
config:
reports:
- filter: true
module: test_ns_1_mod_1
- filter: true
module: test_ns_1_mod_2
pages:
test_ns_1_page_1:
title: Test Namespace 1 Page 1
visible: true
page_layouts:
test_ns_1_page_1_layout_1:
meta:
title: Test Namespace 1 Page 1 Layout 1
test_ns_1_page_1_layout_2:
meta:
title: Test Namespace 1 Page 1 Layout 2
blocks:
- title: Test Namespace 1 Page 1 Block 1
kind: RecordList
options:
module: test_ns_1_mod_1
- title: Test Namespace 1 Page 1 Block 2
kind: Automation
# @todo
# options:
- title: Test Namespace 1 Page 1 Block 3
kind: RecordOrganizer
options:
module: test_ns_1_mod_1
- title: Test Namespace 1 Page 1 Block 4
kind: Chart
options:
chart: test_ns_1_chart_1
- title: Test Namespace 1 Page 1 Block 5
kind: Calendar
options:
feeds:
- options:
module: test_ns_1_mod_1
- title: Test Namespace 1 Page 1 Block 6
kind: Metric
options:
metrics:
- module: test_ns_1_mod_1
- title: Test Namespace 1 Page 1 Block 7
kind: Comment
options:
module: test_ns_1_mod_1
- title: Test Namespace 1 Page 1 Block 8
kind: Progress
options:
module: test_ns_1_mod_1
pages:
test_ns_1_record_page_1:
title: Test Namespace 1 Record Page 1
module: test_ns_1_mod_1
page_layouts:
test_ns_1_record_page_1_layout_1:
meta:
title: Test Namespace 1 Record Page 1 Layout 1
test_ns_1_record_page_1_layout_2:
meta:
title: Test Namespace 1 Record Page 1 Layout 2
application:
test_app_1:
enabled: true
owner: test_user_1
endpoints:
/test/endpoint/1:
method: GET
enabled: true
createdBy: test_user_1
updatedBy: test_user_1
filters:
- ref: r1
kind: prefilter
enabled: true
createdBy: test_user_1
updatedBy: test_user_1
authclient:
test_authclient_1:
secret: 123123
security:
impersonateUser: test_user_1
permittedRoles:
- test_role_1
prohibitedRoles:
- test_role_2
forcedRoles:
- test_role_3
ownedBy: test_user_1
createdBy: test_user_1
updatedBy: test_user_1
queue:
test_queue_1:
consumer: workflow
createdBy: test_user_1
updatedBy: test_user_1
report:
test_report_1:
meta:
name: Test Report 1
Description: Test Report 1 Description
ownedBy: test_user_1
createdBy: test_user_1
updatedBy: test_user_1
role:
test_role_1:
name: Test Role 1
test_role_2:
name: Test Role 2
test_role_3:
name: Test Role 3
template:
test_template_1:
meta:
short: Test Template 1
description: Test Template 1 Description
ownerID: test_user_1
user:
test_user_1:
name: Test User 1
email: test_user_1@example.com
emailConfirmed: true
connection:
test_connection_1:
meta:
name: Test Connection 1
createdBy: test_user_1
updatedBy: test_user_1
sensitivity_level:
test_sensitivity_level_1:
meta:
name: Test Sensitivity Level 1
description: Test sensitivity Level 1 Description
createdBy: test_user_1
updatedBy: test_user_1
workflow:
test_workflow_1:
enabled: true
meta:
name: Test Workflow 1
triggers:
- enabled: true
stepID: 12
resourceType: corteza::compose:namespace

View File

@@ -0,0 +1,20 @@
namespace:
test_ns_1:
name: Test Namespace 1
modules:
test_mod_1:
name: Test Module 1
locale:
en:
corteza::compose:namespace/test_ns_1:
res_tr_1: res_tr_1_text
res_tr_2: res_tr_2_text
corteza::compose:module/test_ns_1/test_mod_1:
res_tr_3: res_tr_3_text
de:
corteza::compose:namespace/test_ns_1:
res_tr_1: res_tr_1_text
res_tr_2: res_tr_2_text
corteza::compose:module/test_ns_1/test_mod_1:
res_tr_3: res_tr_3_text

View File

@@ -0,0 +1,14 @@
namespace:
test_ns_1:
name: Test Namespace 1
modules:
test_mod_1:
name: Test Module 1
locale:
en:
corteza::compose:namespace/test_ns_1:
res_tr_1: res_tr_1_text
res_tr_2: res_tr_2_text
corteza::compose:module/test_ns_1/test_mod_1:
res_tr_3: res_tr_3_text

View File

@@ -0,0 +1,5 @@
locale:
en:
corteza::compose:namespace/test_ns_1:
res_tr_1: res_tr_1_text_duplicate
res_tr_2: res_tr_2_text

View File

@@ -0,0 +1,29 @@
namespace:
test_ns_1:
name: Test Namespace 1
modules:
test_mod_1:
name: Test Module 1
roles:
test_role_1:
name: Test Role 1
test_role_2:
name: Test Role 2
allow:
test_role_1:
corteza::compose:namespace/test_ns_1:
- op2
corteza::compose:module/test_ns_1/*:
- op3
corteza::compose:module/test_ns_1/test_mod_1:
- op4
deny:
test_role_2:
corteza::compose:namespace/test_ns_1:
- op2
corteza::compose:module/test_ns_1/*:
- op3
corteza::compose:module/test_ns_1/test_mod_1:
- op4

View File

@@ -0,0 +1,4 @@
ID,user_name,parent,alt
c11,Daniel Adams,p1,c13
c12,Jane Alex,p2,c11
c13,Fish,p3,c12
1 ID user_name parent alt
2 c11 Daniel Adams p1 c13
3 c12 Jane Alex p2 c11
4 c13 Fish p3 c12

View File

@@ -0,0 +1,49 @@
namespace:
test_ns:
name: Test Namespace
modules:
mod_parent:
name: Test Namespace Module Parent
fields:
name:
title: Name
code:
title: Code
kind: Number
source:
- from: parent_records.csv
key: ID
map:
- column: ID
skip: true
- column: name
field: name
- column: code
field: code
mod_child:
name: Test Namespace Module Child
fields:
user_name:
title: User Name
parent:
title: Parent
kind: Record
options:
module: mod_parent
alt:
title: alt
kind: Record
options:
module: mod_child
source:
- from: child_records.csv
key: ID
map:
- column: ID
skip: true
- column: user_name
field: user_name
- column: parent
field: parent
- column: alt
field: alt

View File

@@ -0,0 +1,4 @@
ID,name,code
p1,Jane Doe,3124
p2,Ana Banana,4153
p3,Qwerty,83172
1 ID name code
2 p1 Jane Doe 3124
3 p2 Ana Banana 4153
4 p3 Qwerty 83172

View File

@@ -0,0 +1,39 @@
namespace:
test_ns:
name: Test Namespace
modules:
test_mod:
name: Test Namespace Module
fields:
name:
title: Name
code:
title: Code
kind: Number
rel_user:
title: Rel User
kind: User
source:
- from: records.csv
key: ID
map:
- column: ID
skip: true
- column: name
field: name
- column: code
field: code
- column: rel_user
field: rel_user
- column: created_by
field: created_by
user:
usr_0:
email: usr_0@test.tld
usr_1:
email: usr_1@test.tld
usr_2:
email: usr_2@test.tld
usr_x:
email: usr_x@test.tld

View File

@@ -0,0 +1,4 @@
ID,name,code,rel_user,created_by
p1,Jane Doe,3124,usr_0,usr_x
p2,Ana Banana,4153,usr_1,usr_x
p3,Qwerty,83172,usr_2,usr_x
1 ID name code rel_user created_by
2 p1 Jane Doe 3124 usr_0 usr_x
3 p2 Ana Banana 4153 usr_1 usr_x
4 p3 Qwerty 83172 usr_2 usr_x

View File

@@ -0,0 +1,4 @@
settings:
auth.external.enabled: true
auth.mail.from-address: info@example.tld
auth.internal.enabled: true