3
0

Add base DAL tests

This commit is contained in:
Tomaž Jerman
2022-06-14 11:33:21 +02:00
parent 11e82354fe
commit 557a573f04
38 changed files with 3041 additions and 603 deletions
+86
View File
@@ -0,0 +1,86 @@
package dal
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestDiff_same(t *testing.T) {
a := &Model{
Attributes: AttributeSet{{
Ident: "F1",
Type: TypeText{},
}},
}
dd := a.Diff(a)
require.Len(t, dd, 0)
}
func TestDiff_wrongAttrType(t *testing.T) {
a := &Model{
Attributes: AttributeSet{{
Ident: "F1",
Type: TypeText{},
}},
}
b := &Model{
Attributes: AttributeSet{{
Ident: "F1",
Type: TypeBlob{},
}},
}
dd := a.Diff(b)
require.Len(t, dd, 1)
require.Equal(t, AttributeTypeMissmatch, dd[0].Type)
}
func TestDiff_removedAttr(t *testing.T) {
a := &Model{
Attributes: AttributeSet{{
Ident: "F1",
Type: TypeText{},
}, {
Ident: "F2",
Type: TypeText{},
}},
}
b := &Model{
Attributes: AttributeSet{{
Ident: "F1",
Type: TypeText{},
}},
}
dd := a.Diff(b)
require.Len(t, dd, 1)
require.Equal(t, AttributeMissing, dd[0].Type)
require.NotNil(t, dd[0].Original)
require.Nil(t, dd[0].Asserted)
}
func TestDiff_addedAttr(t *testing.T) {
a := &Model{
Attributes: AttributeSet{{
Ident: "F1",
Type: TypeText{},
}},
}
b := &Model{
Attributes: AttributeSet{{
Ident: "F1",
Type: TypeText{},
}, {
Ident: "F2",
Type: TypeText{},
}},
}
dd := a.Diff(b)
require.Len(t, dd, 1)
require.Equal(t, AttributeMissing, dd[0].Type)
require.Nil(t, dd[0].Original)
require.NotNil(t, dd[0].Asserted)
}
+95
View File
@@ -0,0 +1,95 @@
package dal
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestSensitivityLevelIncluded(t *testing.T) {
t.Run("does not include; empty", func(t *testing.T) {
ll := sensitivityLevelIndex{}
require.False(t, ll.includes(1))
})
t.Run("does not include; not in there", func(t *testing.T) {
ll := SensitivityLevelIndex(SensitivityLevel{
Handle: "a",
ID: 0,
Level: 1,
})
require.False(t, ll.includes(1))
})
t.Run("includes", func(t *testing.T) {
ll := SensitivityLevelIndex(SensitivityLevel{
Handle: "a",
ID: 1,
Level: 1,
})
require.True(t, ll.includes(1))
})
}
func TestSensitivityLevelSubset(t *testing.T) {
commonLevels := SensitivityLevelSet{{
ID: 1,
Handle: "a",
Level: 1,
}, {
ID: 2,
Handle: "b",
Level: 2,
}, {
ID: 3,
Handle: "c",
Level: 3,
}}
cases := []struct {
name string
a uint64
b uint64
out bool
}{{
name: "lower is zero",
a: 0,
b: 1,
out: true,
}, {
name: "both zero",
a: 0,
b: 0,
out: true,
}, {
name: "upper zero, lower not",
a: 1,
b: 0,
out: false,
}, {
name: "lower less",
a: 1,
b: 2,
out: true,
}, {
name: "equal",
a: 2,
b: 2,
out: true,
}, {
name: "lower higher",
a: 3,
b: 2,
out: false,
}}
ix := SensitivityLevelIndex(commonLevels...)
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
require.Equal(t, c.out, ix.isSubset(c.a, c.b))
})
}
}
-123
View File
@@ -1,123 +0,0 @@
package dal
import (
"context"
"testing"
"time"
"github.com/cortezaproject/corteza-server/compose/types"
"github.com/cortezaproject/corteza-server/pkg/dal"
"github.com/stretchr/testify/require"
)
func RecordCodec(t *testing.T, ctx context.Context, d dal.Connection) {
var (
req = require.New(t)
m = &dal.Model{
Ident: "crs_test_codec",
Attributes: dal.AttributeSet{
&dal.Attribute{Ident: "ID", Type: &dal.TypeID{}, Store: &dal.CodecAlias{Ident: "id"}, PrimaryKey: true},
&dal.Attribute{Ident: "createdAt", Type: &dal.TypeTimestamp{}, Store: &dal.CodecAlias{Ident: "created_at"}},
&dal.Attribute{Ident: "updatedAt", Type: &dal.TypeTimestamp{}, Store: &dal.CodecAlias{Ident: "updated_at"}},
&dal.Attribute{Ident: "vID", Type: &dal.TypeID{}, Store: &dal.CodecRecordValueSetJSON{Ident: "meta"}},
&dal.Attribute{Ident: "vRef", Type: &dal.TypeRef{}, Store: &dal.CodecRecordValueSetJSON{Ident: "meta"}},
&dal.Attribute{Ident: "vTimestamp", Type: &dal.TypeTimestamp{}, Store: &dal.CodecRecordValueSetJSON{Ident: "meta"}},
&dal.Attribute{Ident: "vTime", Type: &dal.TypeTime{}, Store: &dal.CodecRecordValueSetJSON{Ident: "meta"}},
&dal.Attribute{Ident: "vDate", Type: &dal.TypeDate{}, Store: &dal.CodecRecordValueSetJSON{Ident: "meta"}},
&dal.Attribute{Ident: "vNumber", Type: &dal.TypeNumber{}, Store: &dal.CodecRecordValueSetJSON{Ident: "meta"}},
&dal.Attribute{Ident: "vText", Type: &dal.TypeText{}, Store: &dal.CodecRecordValueSetJSON{Ident: "meta"}},
&dal.Attribute{Ident: "vBoolean_T", Type: &dal.TypeBoolean{}, Store: &dal.CodecRecordValueSetJSON{Ident: "meta"}},
&dal.Attribute{Ident: "vBoolean_F", Type: &dal.TypeBoolean{}, Store: &dal.CodecRecordValueSetJSON{Ident: "meta"}},
&dal.Attribute{Ident: "vEnum", Type: &dal.TypeEnum{}, Store: &dal.CodecRecordValueSetJSON{Ident: "meta"}},
&dal.Attribute{Ident: "vGeometry", Type: &dal.TypeGeometry{}, Store: &dal.CodecRecordValueSetJSON{Ident: "meta"}},
&dal.Attribute{Ident: "vJSON", Type: &dal.TypeJSON{}, Store: &dal.CodecRecordValueSetJSON{Ident: "meta"}},
&dal.Attribute{Ident: "vBlob", Type: &dal.TypeBlob{}, Store: &dal.CodecRecordValueSetJSON{Ident: "meta"}},
&dal.Attribute{Ident: "vUUID", Type: &dal.TypeUUID{}, Store: &dal.CodecRecordValueSetJSON{Ident: "meta"}},
&dal.Attribute{Ident: "pID", Type: &dal.TypeID{}, Store: &dal.CodecPlain{}},
&dal.Attribute{Ident: "pRef", Type: &dal.TypeRef{}, Store: &dal.CodecPlain{}},
&dal.Attribute{Ident: "pTimestamp_TZT", Type: &dal.TypeTimestamp{Timezone: true}, Store: &dal.CodecPlain{}},
&dal.Attribute{Ident: "pTimestamp_TZF", Type: &dal.TypeTimestamp{Timezone: false}, Store: &dal.CodecPlain{}},
&dal.Attribute{Ident: "pTime", Type: &dal.TypeTime{}, Store: &dal.CodecPlain{}},
&dal.Attribute{Ident: "pDate", Type: &dal.TypeDate{}, Store: &dal.CodecPlain{}},
&dal.Attribute{Ident: "pNumber", Type: &dal.TypeNumber{}, Store: &dal.CodecPlain{}},
&dal.Attribute{Ident: "pText", Type: &dal.TypeText{}, Store: &dal.CodecPlain{}},
&dal.Attribute{Ident: "pBoolean_T", Type: &dal.TypeBoolean{}, Store: &dal.CodecPlain{}},
&dal.Attribute{Ident: "pBoolean_F", Type: &dal.TypeBoolean{}, Store: &dal.CodecPlain{}},
&dal.Attribute{Ident: "pEnum", Type: &dal.TypeEnum{}, Store: &dal.CodecPlain{}},
&dal.Attribute{Ident: "pGeometry", Type: &dal.TypeGeometry{}, Store: &dal.CodecPlain{}},
&dal.Attribute{Ident: "pJSON", Type: &dal.TypeJSON{}, Store: &dal.CodecPlain{}},
&dal.Attribute{Ident: "pBlob", Type: &dal.TypeBlob{}, Store: &dal.CodecPlain{}},
&dal.Attribute{Ident: "pUUID", Type: &dal.TypeUUID{}, Store: &dal.CodecPlain{}},
},
}
rIn = types.Record{ID: 42}
err error
rOut *types.Record
piTime time.Time
)
piTime, err = time.Parse("2006-01-02T15:04:05", "2006-01-02T15:04:05")
req.NoError(err)
piTime = piTime.UTC()
rIn.CreatedAt = piTime
rIn.UpdatedAt = &piTime
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vID", Value: "34324"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vRef", Value: "32423"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vTimestamp", Value: "2022-01-01T10:10:10+02:00"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vTime", Value: "04:10:10+04:00"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vDate", Value: "2022-01-01"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vNumber", Value: "2423423"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vText", Value: "lorm ipsum "})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vBoolean_T", Value: "true"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vBoolean_F", Value: "false"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vEnum", Value: "abc"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vGeometry", Value: `{"lat":1,"lng":1}`})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vJSON", Value: `[{"bool":true"}]`})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vBlob", Value: "0110101"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vUUID", Value: "ba485865-54f9-44de-bde8-6965556c022a"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "pID", Value: "34324"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "pRef", Value: "32423"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "pTimestamp_TZF", Value: "2022-02-01T10:10:10"})
// @todo how (if at all) should we know if underlying DB supports timezone?
//rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "pTimestamp_TZT", Value: "2022-02-01T10:10:10"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "pTime", Value: "06:06:06"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "pDate", Value: "2022-01-01"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "pNumber", Value: "2423423"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "pText", Value: "lorm ipsum "})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "pBoolean_T", Value: "true"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "pBoolean_F", Value: "false"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "pEnum", Value: "abc"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "pGeometry", Value: `{"lat":1,"lng":1}`})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "pJSON", Value: `[{"bool":true"}]`})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "pBlob", Value: "0110101"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "pUUID", Value: "ba485865-54f9-44de-bde8-6965556c022a"})
rIn.Values = rIn.Values.GetClean()
req.NoError(d.Create(ctx, m, &rIn))
rOut = new(types.Record)
req.NoError(d.Lookup(ctx, m, dal.PKValues{"id": rIn.ID}, rOut))
{
// normalize timezone on timestamps
rOut.CreatedAt = rOut.CreatedAt.UTC()
aux := rOut.UpdatedAt.UTC()
rOut.UpdatedAt = &aux
}
for _, attr := range m.Attributes {
vIn, err := rIn.GetValue(attr.Ident, 0)
req.NoError(err)
vOut, err := rOut.GetValue(attr.Ident, 0)
req.NoError(err)
req.Equal(vIn, vOut, "values for attribute %q are not equal", attr.Ident)
}
}
+66
View File
@@ -0,0 +1,66 @@
package dal
import (
"context"
"testing"
"github.com/cortezaproject/corteza-server/compose/types"
"github.com/cortezaproject/corteza-server/pkg/dal"
"github.com/cortezaproject/corteza-server/pkg/dal/capabilities"
)
func Test_dal_codec_alias(t *testing.T) {
model := &dal.Model{
Ident: "compose_record_partitioned",
Attributes: dal.AttributeSet{
&dal.Attribute{Ident: "ID", Type: &dal.TypeID{}, Store: &dal.CodecAlias{Ident: "id"}, PrimaryKey: true},
&dal.Attribute{Ident: "intr_vID", Type: &dal.TypeID{}, Store: &dal.CodecAlias{Ident: "vID"}},
&dal.Attribute{Ident: "intr_vRef", Type: &dal.TypeRef{}, Store: &dal.CodecAlias{Ident: "vRef"}},
&dal.Attribute{Ident: "intr_vTimestamp", Type: &dal.TypeTimestamp{}, Store: &dal.CodecAlias{Ident: "vTimestamp"}},
&dal.Attribute{Ident: "intr_vTime", Type: &dal.TypeTime{}, Store: &dal.CodecAlias{Ident: "vTime"}},
&dal.Attribute{Ident: "intr_vDate", Type: &dal.TypeDate{}, Store: &dal.CodecAlias{Ident: "vDate"}},
&dal.Attribute{Ident: "intr_vNumber", Type: &dal.TypeNumber{}, Store: &dal.CodecAlias{Ident: "vNumber"}},
&dal.Attribute{Ident: "intr_vText", Type: &dal.TypeText{}, Store: &dal.CodecAlias{Ident: "vText"}},
&dal.Attribute{Ident: "intr_vBoolean_T", Type: &dal.TypeBoolean{}, Store: &dal.CodecAlias{Ident: "vBoolean_T"}},
&dal.Attribute{Ident: "intr_vBoolean_F", Type: &dal.TypeBoolean{}, Store: &dal.CodecAlias{Ident: "vBoolean_F"}},
&dal.Attribute{Ident: "intr_vEnum", Type: &dal.TypeEnum{}, Store: &dal.CodecAlias{Ident: "vEnum"}},
&dal.Attribute{Ident: "intr_vGeometry", Type: &dal.TypeGeometry{}, Store: &dal.CodecAlias{Ident: "vGeometry"}},
&dal.Attribute{Ident: "intr_vJSON", Type: &dal.TypeJSON{}, Store: &dal.CodecAlias{Ident: "vJSON"}},
&dal.Attribute{Ident: "intr_vBlob", Type: &dal.TypeBlob{}, Store: &dal.CodecAlias{Ident: "vBlob"}},
&dal.Attribute{Ident: "intr_vUUID", Type: &dal.TypeUUID{}, Store: &dal.CodecAlias{Ident: "vUUID"}},
},
}
var (
rIn = types.Record{ID: 42}
rOut = types.Record{}
)
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "intr_vID", Value: "34324"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "intr_vRef", Value: "32423"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "intr_vTimestamp", Value: "2022-01-01T10:10:10"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "intr_vTime", Value: "04:10:10"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "intr_vDate", Value: "2022-01-01"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "intr_vNumber", Value: "2423423"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "intr_vText", Value: "lorm ipsum "})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "intr_vBoolean_T", Value: "true"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "intr_vBoolean_F", Value: "false"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "intr_vEnum", Value: "abc"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "intr_vGeometry", Value: `{"lat":1,"lng":1}`})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "intr_vJSON", Value: `[{"bool":true"}]`})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "intr_vBlob", Value: "0110101"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "intr_vUUID", Value: "ba485865-54f9-44de-bde8-6965556c022a"})
bootstrap(t, func(ctx context.Context, t *testing.T, h helper, svc dalService) {
h.a.NoError(svc.CreateModel(ctx, model))
h.a.NoError(svc.Create(ctx, model.ToFilter(), capabilities.CreateCapabilities(model.Capabilities...), &rIn))
h.a.NoError(svc.Lookup(ctx, model.ToFilter(), capabilities.LookupCapabilities(model.Capabilities...), dal.PKValues{"id": rIn.ID}, &rOut))
for _, inVal := range rIn.Values {
outVal := rOut.Values.Get(inVal.Name, 0)
h.a.Equal(inVal.Value, outVal.Value, "field", inVal.Name)
}
})
}
+66
View File
@@ -0,0 +1,66 @@
package dal
import (
"context"
"testing"
"github.com/cortezaproject/corteza-server/compose/types"
"github.com/cortezaproject/corteza-server/pkg/dal"
"github.com/cortezaproject/corteza-server/pkg/dal/capabilities"
)
func Test_dal_codec_json(t *testing.T) {
model := &dal.Model{
Ident: "compose_record",
Attributes: dal.AttributeSet{
&dal.Attribute{Ident: "ID", Type: &dal.TypeID{}, Store: &dal.CodecAlias{Ident: "id"}, PrimaryKey: true},
&dal.Attribute{Ident: "vID", Type: &dal.TypeID{}, Store: &dal.CodecRecordValueSetJSON{Ident: "values"}},
&dal.Attribute{Ident: "vRef", Type: &dal.TypeRef{}, Store: &dal.CodecRecordValueSetJSON{Ident: "values"}},
&dal.Attribute{Ident: "vTimestamp", Type: &dal.TypeTimestamp{}, Store: &dal.CodecRecordValueSetJSON{Ident: "values"}},
&dal.Attribute{Ident: "vTime", Type: &dal.TypeTime{}, Store: &dal.CodecRecordValueSetJSON{Ident: "values"}},
&dal.Attribute{Ident: "vDate", Type: &dal.TypeDate{}, Store: &dal.CodecRecordValueSetJSON{Ident: "values"}},
&dal.Attribute{Ident: "vNumber", Type: &dal.TypeNumber{}, Store: &dal.CodecRecordValueSetJSON{Ident: "values"}},
&dal.Attribute{Ident: "vText", Type: &dal.TypeText{}, Store: &dal.CodecRecordValueSetJSON{Ident: "values"}},
&dal.Attribute{Ident: "vBoolean_T", Type: &dal.TypeBoolean{}, Store: &dal.CodecRecordValueSetJSON{Ident: "values"}},
&dal.Attribute{Ident: "vBoolean_F", Type: &dal.TypeBoolean{}, Store: &dal.CodecRecordValueSetJSON{Ident: "values"}},
&dal.Attribute{Ident: "vEnum", Type: &dal.TypeEnum{}, Store: &dal.CodecRecordValueSetJSON{Ident: "values"}},
&dal.Attribute{Ident: "vGeometry", Type: &dal.TypeGeometry{}, Store: &dal.CodecRecordValueSetJSON{Ident: "values"}},
&dal.Attribute{Ident: "vJSON", Type: &dal.TypeJSON{}, Store: &dal.CodecRecordValueSetJSON{Ident: "values"}},
&dal.Attribute{Ident: "vBlob", Type: &dal.TypeBlob{}, Store: &dal.CodecRecordValueSetJSON{Ident: "values"}},
&dal.Attribute{Ident: "vUUID", Type: &dal.TypeUUID{}, Store: &dal.CodecRecordValueSetJSON{Ident: "values"}},
},
}
var (
rIn = types.Record{ID: 42}
rOut = types.Record{}
)
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vID", Value: "34324"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vRef", Value: "32423"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vTimestamp", Value: "2022-01-01T10:10:10"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vTime", Value: "04:10:10"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vDate", Value: "2022-01-01"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vNumber", Value: "2423423"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vText", Value: "lorm ipsum "})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vBoolean_T", Value: "true"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vBoolean_F", Value: "false"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vEnum", Value: "abc"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vGeometry", Value: `{"lat":1,"lng":1}`})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vJSON", Value: `[{"bool":true"}]`})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vBlob", Value: "0110101"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vUUID", Value: "ba485865-54f9-44de-bde8-6965556c022a"})
bootstrap(t, func(ctx context.Context, t *testing.T, h helper, svc dalService) {
h.a.NoError(svc.CreateModel(ctx, model))
h.a.NoError(svc.Create(ctx, model.ToFilter(), capabilities.CreateCapabilities(model.Capabilities...), &rIn))
h.a.NoError(svc.Lookup(ctx, model.ToFilter(), capabilities.LookupCapabilities(model.Capabilities...), dal.PKValues{"id": rIn.ID}, &rOut))
for _, inVal := range rIn.Values {
outVal := rOut.Values.Get(inVal.Name, 0)
h.a.Equal(inVal.Value, outVal.Value, "field", inVal.Name)
}
})
}
+136
View File
@@ -0,0 +1,136 @@
package dal
import (
"context"
"testing"
"github.com/cortezaproject/corteza-server/compose/types"
"github.com/cortezaproject/corteza-server/pkg/dal"
"github.com/cortezaproject/corteza-server/pkg/dal/capabilities"
)
func Test_dal_codec_plain(t *testing.T) {
model := &dal.Model{
Ident: "compose_record_partitioned",
Attributes: dal.AttributeSet{
&dal.Attribute{Ident: "ID", Type: &dal.TypeID{}, Store: &dal.CodecAlias{Ident: "id"}, PrimaryKey: true},
&dal.Attribute{Ident: "vID", Type: &dal.TypeID{}, Store: &dal.CodecPlain{}},
&dal.Attribute{Ident: "vRef", Type: &dal.TypeRef{}, Store: &dal.CodecPlain{}},
&dal.Attribute{Ident: "vTimestamp", Type: &dal.TypeTimestamp{}, Store: &dal.CodecPlain{}},
&dal.Attribute{Ident: "vTime", Type: &dal.TypeTime{}, Store: &dal.CodecPlain{}},
&dal.Attribute{Ident: "vDate", Type: &dal.TypeDate{}, Store: &dal.CodecPlain{}},
&dal.Attribute{Ident: "vNumber", Type: &dal.TypeNumber{}, Store: &dal.CodecPlain{}},
&dal.Attribute{Ident: "vText", Type: &dal.TypeText{}, Store: &dal.CodecPlain{}},
&dal.Attribute{Ident: "vBoolean_T", Type: &dal.TypeBoolean{}, Store: &dal.CodecPlain{}},
&dal.Attribute{Ident: "vBoolean_F", Type: &dal.TypeBoolean{}, Store: &dal.CodecPlain{}},
&dal.Attribute{Ident: "vEnum", Type: &dal.TypeEnum{}, Store: &dal.CodecPlain{}},
&dal.Attribute{Ident: "vGeometry", Type: &dal.TypeGeometry{}, Store: &dal.CodecPlain{}},
&dal.Attribute{Ident: "vJSON", Type: &dal.TypeJSON{}, Store: &dal.CodecPlain{}},
&dal.Attribute{Ident: "vBlob", Type: &dal.TypeBlob{}, Store: &dal.CodecPlain{}},
&dal.Attribute{Ident: "vUUID", Type: &dal.TypeUUID{}, Store: &dal.CodecPlain{}},
},
}
var (
rIn = types.Record{ID: 42}
rOut = types.Record{}
)
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vID", Value: "34324"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vRef", Value: "32423"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vTimestamp", Value: "2022-01-01T10:10:10"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vTime", Value: "04:10:10"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vDate", Value: "2022-01-01"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vNumber", Value: "2423423"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vText", Value: "lorm ipsum "})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vBoolean_T", Value: "true"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vBoolean_F", Value: "false"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vEnum", Value: "abc"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vGeometry", Value: `{"lat":1,"lng":1}`})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vJSON", Value: `[{"bool":true"}]`})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vBlob", Value: "0110101"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vUUID", Value: "ba485865-54f9-44de-bde8-6965556c022a"})
bootstrap(t, func(ctx context.Context, t *testing.T, h helper, svc dalService) {
h.a.NoError(svc.CreateModel(ctx, model))
h.a.NoError(svc.Create(ctx, model.ToFilter(), capabilities.CreateCapabilities(model.Capabilities...), &rIn))
h.a.NoError(svc.Lookup(ctx, model.ToFilter(), capabilities.LookupCapabilities(model.Capabilities...), dal.PKValues{"id": rIn.ID}, &rOut))
for _, inVal := range rIn.Values {
outVal := rOut.Values.Get(inVal.Name, 0)
h.a.Equal(inVal.Value, outVal.Value, "field", inVal.Name)
}
})
}
func benchmark_dal_codec_plain(b *testing.B, count int) {
model := &dal.Model{
Ident: "compose_record_partitioned",
Attributes: dal.AttributeSet{
&dal.Attribute{Ident: "ID", Type: &dal.TypeID{}, Store: &dal.CodecAlias{Ident: "id"}, PrimaryKey: true},
&dal.Attribute{Ident: "vID", Type: &dal.TypeID{}, Store: &dal.CodecPlain{}},
&dal.Attribute{Ident: "vRef", Type: &dal.TypeRef{}, Store: &dal.CodecPlain{}},
&dal.Attribute{Ident: "vTimestamp", Type: &dal.TypeTimestamp{}, Store: &dal.CodecPlain{}},
&dal.Attribute{Ident: "vTime", Type: &dal.TypeTime{}, Store: &dal.CodecPlain{}},
&dal.Attribute{Ident: "vDate", Type: &dal.TypeDate{}, Store: &dal.CodecPlain{}},
&dal.Attribute{Ident: "vNumber", Type: &dal.TypeNumber{}, Store: &dal.CodecPlain{}},
&dal.Attribute{Ident: "vText", Type: &dal.TypeText{}, Store: &dal.CodecPlain{}},
&dal.Attribute{Ident: "vBoolean_T", Type: &dal.TypeBoolean{}, Store: &dal.CodecPlain{}},
&dal.Attribute{Ident: "vBoolean_F", Type: &dal.TypeBoolean{}, Store: &dal.CodecPlain{}},
&dal.Attribute{Ident: "vEnum", Type: &dal.TypeEnum{}, Store: &dal.CodecPlain{}},
&dal.Attribute{Ident: "vGeometry", Type: &dal.TypeGeometry{}, Store: &dal.CodecPlain{}},
&dal.Attribute{Ident: "vJSON", Type: &dal.TypeJSON{}, Store: &dal.CodecPlain{}},
&dal.Attribute{Ident: "vBlob", Type: &dal.TypeBlob{}, Store: &dal.CodecPlain{}},
&dal.Attribute{Ident: "vUUID", Type: &dal.TypeUUID{}, Store: &dal.CodecPlain{}},
},
}
var (
rIn = types.Record{ID: 42}
)
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vID", Value: "34324"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vRef", Value: "32423"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vTimestamp", Value: "2022-01-01T10:10:10"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vTime", Value: "04:10:10"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vDate", Value: "2022-01-01"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vNumber", Value: "2423423"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vText", Value: "lorm ipsum "})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vBoolean_T", Value: "true"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vBoolean_F", Value: "false"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vEnum", Value: "abc"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vGeometry", Value: `{"lat":1,"lng":1}`})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vJSON", Value: `[{"bool":true"}]`})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vBlob", Value: "0110101"})
rIn.Values = rIn.Values.Set(&types.RecordValue{Name: "vUUID", Value: "ba485865-54f9-44de-bde8-6965556c022a"})
bootstrapBenchmark(b, func(ctx context.Context, b *testing.B, h helper, svc dalService) {
h.a.NoError(svc.CreateModel(ctx, model))
ctr := uint64(1)
for rep := 0; rep < b.N; rep++ {
insert := make([]dal.ValueGetter, 0, 100)
for i := 0; i < 100; i++ {
tmp := rIn
tmp.ID = ctr
ctr++
insert = append(insert, &tmp)
}
b.StartTimer()
h.a.NoError(svc.Create(ctx, model.ToFilter(), capabilities.CreateCapabilities(model.Capabilities...), insert...))
b.StopTimer()
}
})
}
func Benchmark_dal_codec_plain_1(b *testing.B) { benchmark_dal_codec_plain(b, 1) }
func Benchmark_dal_codec_plain_10(b *testing.B) { benchmark_dal_codec_plain(b, 10) }
func Benchmark_dal_codec_plain_100(b *testing.B) { benchmark_dal_codec_plain(b, 100) }
func Benchmark_dal_codec_plain_1000(b *testing.B) { benchmark_dal_codec_plain(b, 1000) }
func Benchmark_dal_codec_plain_10000(b *testing.B) { benchmark_dal_codec_plain(b, 10000) }
+209
View File
@@ -0,0 +1,209 @@
package dal
import (
"encoding/json"
"fmt"
"net/http"
"testing"
"time"
"github.com/cortezaproject/corteza-server/compose/types"
"github.com/cortezaproject/corteza-server/pkg/dal/capabilities"
"github.com/cortezaproject/corteza-server/tests/helpers"
)
func Test_dal_crud_compose_record_create(t *testing.T) {
h := newHelperT(t)
defer h.cleanupDal()
ctx := h.secCtx()
helpers.AllowMe(h, types.NamespaceRbacResource(0), "module.create")
helpers.AllowMe(h, types.NamespaceRbacResource(0), "modules.search")
helpers.AllowMe(h, types.ModuleRbacResource(0, 0), "read")
helpers.AllowMe(h, types.ModuleRbacResource(0, 0), "update")
helpers.AllowMe(h, types.ModuleRbacResource(0, 0), "delete")
helpers.AllowMe(h, types.ModuleRbacResource(0, 0), "record.create")
helpers.AllowMe(h, types.ModuleRbacResource(0, 0), "records.search")
helpers.AllowMe(h, types.ModuleFieldRbacResource(0, 0, 0), "record.value.update")
helpers.AllowMe(h, types.ModuleFieldRbacResource(0, 0, 0), "record.value.read")
ns := h.createNamespace("test")
module := createModuleFromGenerics(ctx, t, "ok_module.json", ns.ID, &types.ModelConfig{
ConnectionID: 0,
Capabilities: capabilities.FullCapabilities(),
})
// create
h.apiInit().
Post(fmt.Sprintf("/compose/namespace/%d/module/%d/record/", ns.ID, module.ID)).
Body(loadRequestFromGenerics(t, "ok_record.json")).
Header("Accept", "application/json").
Header("Content-Type", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
End()
rsp := h.apiInit().
Get(fmt.Sprintf("/compose/namespace/%d/module/%d/record/", ns.ID, module.ID)).
Header("Accept", "application/json").
Header("Content-Type", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
End()
aux := composeRecordSearchRestRsp{}
dd := json.NewDecoder(rsp.Response.Body)
h.a.NoError(dd.Decode(&aux))
r := aux.Response.Set[0]
h.a.NotEqual(uint64(0), r.ID)
h.a.Equal(module.ID, r.ModuleID)
h.a.Equal(module.NamespaceID, r.NamespaceID)
h.a.Equal(h.cUser.ID, r.OwnedBy)
h.a.NotEqual(time.Time{}, r.CreatedAt)
h.a.Equal(h.cUser.ID, r.CreatedBy)
h.a.Nil(r.UpdatedAt)
h.a.Equal(uint64(0), r.UpdatedBy)
h.a.Nil(r.DeletedAt)
h.a.Equal(uint64(0), r.DeletedBy)
h.a.Equal("me@test.tld", r.Values.Get("email", 0).Value)
h.a.Equal("Me", r.Values.Get("name", 0).Value)
h.a.Equal("42", r.Values.Get("a_number", 0).Value)
}
func Test_dal_crud_compose_record_update(t *testing.T) {
h := newHelperT(t)
defer h.cleanupDal()
ctx := h.secCtx()
helpers.AllowMe(h, types.NamespaceRbacResource(0), "module.create")
helpers.AllowMe(h, types.NamespaceRbacResource(0), "modules.search")
helpers.AllowMe(h, types.ModuleRbacResource(0, 0), "read")
helpers.AllowMe(h, types.ModuleRbacResource(0, 0), "update")
helpers.AllowMe(h, types.ModuleRbacResource(0, 0), "delete")
helpers.AllowMe(h, types.ModuleRbacResource(0, 0), "record.create")
helpers.AllowMe(h, types.ModuleRbacResource(0, 0), "records.search")
helpers.AllowMe(h, types.ModuleFieldRbacResource(0, 0, 0), "record.value.update")
helpers.AllowMe(h, types.ModuleFieldRbacResource(0, 0, 0), "record.value.read")
helpers.AllowMe(h, types.RecordRbacResource(0, 0, 0), "update")
ns := h.createNamespace("test")
module := createModuleFromGenerics(ctx, t, "ok_module.json", ns.ID, &types.ModelConfig{
ConnectionID: 0,
Capabilities: capabilities.FullCapabilities(),
})
record := createRecordFromGenerics(ctx, t, "ok_record.json", ns.ID, module.ID)
h.apiInit().
Post(fmt.Sprintf("/compose/namespace/%d/module/%d/record/%d", ns.ID, module.ID, record.ID)).
Body(loadRequestFromGenerics(t, "ok_record_update.json")).
Header("Accept", "application/json").
Header("Content-Type", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
End()
rsp := h.apiInit().
Get(fmt.Sprintf("/compose/namespace/%d/module/%d/record/", ns.ID, module.ID)).
Header("Accept", "application/json").
Header("Content-Type", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
End()
aux := composeRecordSearchRestRsp{}
dd := json.NewDecoder(rsp.Response.Body)
h.a.NoError(dd.Decode(&aux))
r := aux.Response.Set[0]
h.a.NotEqual(uint64(0), r.ID)
h.a.Equal(module.ID, r.ModuleID)
h.a.Equal(module.NamespaceID, r.NamespaceID)
h.a.Equal(h.cUser.ID, r.OwnedBy)
h.a.NotEqual(time.Time{}, r.CreatedAt)
h.a.Equal(h.cUser.ID, r.CreatedBy)
h.a.NotNil(r.UpdatedAt)
h.a.Equal(h.cUser.ID, r.UpdatedBy)
h.a.Nil(r.DeletedAt)
h.a.Equal(uint64(0), r.DeletedBy)
h.a.Equal("me+updated@test.tld", r.Values.Get("email", 0).Value)
h.a.Equal("Me updated", r.Values.Get("name", 0).Value)
h.a.Equal("43", r.Values.Get("a_number", 0).Value)
}
func Test_dal_crud_compose_record_delete(t *testing.T) {
h := newHelperT(t)
defer h.cleanupDal()
ctx := h.secCtx()
helpers.AllowMe(h, types.NamespaceRbacResource(0), "module.create")
helpers.AllowMe(h, types.NamespaceRbacResource(0), "modules.search")
helpers.AllowMe(h, types.ModuleRbacResource(0, 0), "read")
helpers.AllowMe(h, types.ModuleRbacResource(0, 0), "update")
helpers.AllowMe(h, types.ModuleRbacResource(0, 0), "delete")
helpers.AllowMe(h, types.ModuleRbacResource(0, 0), "record.create")
helpers.AllowMe(h, types.ModuleRbacResource(0, 0), "records.search")
helpers.AllowMe(h, types.ModuleFieldRbacResource(0, 0, 0), "record.value.update")
helpers.AllowMe(h, types.ModuleFieldRbacResource(0, 0, 0), "record.value.read")
helpers.AllowMe(h, types.RecordRbacResource(0, 0, 0), "delete")
helpers.AllowMe(h, types.RecordRbacResource(0, 0, 0), "read")
ns := h.createNamespace("test")
module := createModuleFromGenerics(ctx, t, "ok_module.json", ns.ID, &types.ModelConfig{
ConnectionID: 0,
Capabilities: capabilities.FullCapabilities(),
})
record := createRecordFromGenerics(ctx, t, "ok_record.json", ns.ID, module.ID)
h.apiInit().
Delete(fmt.Sprintf("/compose/namespace/%d/module/%d/record/%d", ns.ID, module.ID, record.ID)).
Header("Accept", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
End()
rsp := h.apiInit().
Get(fmt.Sprintf("/compose/namespace/%d/module/%d/record/%d", ns.ID, module.ID, record.ID)).
Header("Accept", "application/json").
Header("Content-Type", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
End()
aux := composeRecordRestRsp{}
dd := json.NewDecoder(rsp.Response.Body)
h.a.NoError(dd.Decode(&aux))
r := aux.Response
h.a.NotEqual(uint64(0), r.ID)
h.a.Equal(module.ID, r.ModuleID)
h.a.Equal(module.NamespaceID, r.NamespaceID)
h.a.Equal(h.cUser.ID, r.OwnedBy)
h.a.NotEqual(time.Time{}, r.CreatedAt)
h.a.Equal(h.cUser.ID, r.CreatedBy)
h.a.Nil(r.UpdatedAt)
h.a.Equal(uint64(0), r.UpdatedBy)
h.a.NotNil(r.DeletedAt)
h.a.Equal(h.cUser.ID, r.DeletedBy)
}
+283
View File
@@ -0,0 +1,283 @@
package dal
import (
"fmt"
"net/http"
"testing"
"github.com/cortezaproject/corteza-server/system/types"
"github.com/cortezaproject/corteza-server/tests/helpers"
jsonpath "github.com/steinfletcher/apitest-jsonpath"
)
func Test_dal_crud_connection_list(t *testing.T) {
h := newHelperT(t)
defer h.cleanupDal()
helpers.AllowMe(h, types.ComponentRbacResource(), "dal-connections.search")
helpers.AllowMe(h, types.DalConnectionRbacResource(0), "read")
h.apiInit().
Get("/system/dal/connections/").
Header("Accept", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
// This will be the primary one
Assert(jsonpath.Len("$.response.set", 1)).
End()
}
func Test_dal_crud_connection_list_forbidden(t *testing.T) {
h := newHelperT(t)
defer h.cleanupDal()
h.apiInit().
Get("/system/dal/connections/").
Header("Accept", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertError("dalConnection.errors.notAllowedToSearch")).
End()
}
func Test_dal_crud_connection_list_forbidden_read(t *testing.T) {
h := newHelperT(t)
defer h.cleanupDal()
helpers.AllowMe(h, types.ComponentRbacResource(), "dal-connections.search")
h.apiInit().
Get("/system/dal/connections/").
Header("Accept", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(jsonpath.Len("$.response.set", 0)).
End()
}
func Test_dal_crud_connection_create(t *testing.T) {
h := newHelperT(t)
defer h.cleanupDal()
helpers.AllowMe(h, types.ComponentRbacResource(), "dal-connection.create")
h.apiInit().
Post("/system/dal/connections/").
Body(loadRequestFromGenerics(t, "ok_connection.json")).
Header("Accept", "application/json").
Header("Content-Type", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
Assert(jsonpath.Present("$.response.connectionID")).
End()
}
func Test_dal_crud_connection_create_invalid_type(t *testing.T) {
h := newHelperT(t)
defer h.cleanupDal()
helpers.AllowMe(h, types.ComponentRbacResource(), "dal-connection.create")
h.apiInit().
Post("/system/dal/connections/").
Body(loadRequestFromGenerics(t, "nok_connection_invalid_type.json")).
Header("Accept", "application/json").
Header("Content-Type", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertErrorP("corteza::system:primary_dal_connection")).
End()
}
func Test_dal_crud_connection_create_forbidden(t *testing.T) {
h := newHelperT(t)
defer h.cleanupDal()
h.apiInit().
Post("/system/dal/connections/").
Body(loadRequestFromGenerics(t, "ok_connection.json")).
Header("Accept", "application/json").
Header("Content-Type", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertError("dalConnection.errors.notAllowedToCreate")).
End()
}
func Test_dal_crud_connection_update(t *testing.T) {
h := newHelperT(t)
defer h.cleanupDal()
sl := h.createDalConnection(&types.DalConnection{
Handle: "test_connection",
})
helpers.AllowMe(h, types.DalConnectionRbacResource(0), "update")
h.apiInit().
Put(fmt.Sprintf("/system/dal/connections/%d", sl.ID)).
Header("Accept", "application/json").
Header("Content-Type", "application/json").
Body(loadRequestFromGenerics(t, "ok_connection_update.json")).
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
Assert(jsonpath.Equal("$.response.handle", "test_connection_edited")).
End()
}
func Test_dal_crud_connection_update_primary(t *testing.T) {
h := newHelperT(t)
defer h.cleanupDal()
sl := h.getPrimaryConnection()
helpers.AllowMe(h, types.DalConnectionRbacResource(0), "update")
h.apiInit().
Put(fmt.Sprintf("/system/dal/connections/%d", sl.ID)).
Header("Accept", "application/json").
Header("Content-Type", "application/json").
Body(loadRequestFromScenario(t, "connection.json")).
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
Assert(jsonpath.Equal("$.response.name", "Primary Connection EDITED")).
End()
}
func Test_dal_crud_connection_update_forbidden(t *testing.T) {
h := newHelperT(t)
defer h.cleanupDal()
sl := h.createDalConnection(&types.DalConnection{
Handle: "test_connection",
})
h.apiInit().
Put(fmt.Sprintf("/system/dal/connections/%d", sl.ID)).
Header("Accept", "application/json").
Header("Content-Type", "application/json").
Body(loadRequestFromGenerics(t, "ok_connection_update.json")).
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertError("dalConnection.errors.notAllowedToUpdate")).
End()
}
func Test_dal_crud_connection_read(t *testing.T) {
h := newHelperT(t)
defer h.cleanupDal()
sl := h.createDalConnection(&types.DalConnection{
Handle: "test_connection",
})
helpers.AllowMe(h, types.DalConnectionRbacResource(0), "read")
h.apiInit().
Get(fmt.Sprintf("/system/dal/connections/%d", sl.ID)).
Header("Accept", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
Assert(jsonpath.Present("$.response.connectionID")).
End()
}
func Test_dal_crud_connection_read_forbiden(t *testing.T) {
h := newHelperT(t)
defer h.cleanupDal()
sl := h.createDalConnection(&types.DalConnection{
Handle: "test_connection",
})
h.apiInit().
Get(fmt.Sprintf("/system/dal/connections/%d", sl.ID)).
Header("Accept", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertError("dalConnection.errors.notAllowedToRead")).
End()
}
func Test_dal_crud_connection_delete(t *testing.T) {
h := newHelperT(t)
defer h.cleanupDal()
sl := h.createDalConnection(&types.DalConnection{
Handle: "test_connection",
})
helpers.AllowMe(h, types.DalConnectionRbacResource(0), "delete")
h.apiInit().
Delete(fmt.Sprintf("/system/dal/connections/%d", sl.ID)).
Header("Accept", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
Assert(jsonpath.Equal("$.success.message", "OK")).
End()
}
func Test_dal_crud_connection_delete_forbidden(t *testing.T) {
h := newHelperT(t)
defer h.cleanupDal()
sl := h.createDalConnection(&types.DalConnection{
Handle: "test_connection",
})
h.apiInit().
Delete(fmt.Sprintf("/system/dal/connections/%d", sl.ID)).
Header("Accept", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertError("dalConnection.errors.notAllowedToDelete")).
End()
}
func Test_dal_crud_connection_undelete(t *testing.T) {
h := newHelperT(t)
defer h.cleanupDal()
sl := h.createDalConnection(&types.DalConnection{
Handle: "test_connection",
DeletedAt: &h.cUser.CreatedAt,
DeletedBy: h.cUser.ID,
})
helpers.AllowMe(h, types.DalConnectionRbacResource(0), "delete")
h.apiInit().
Post(fmt.Sprintf("/system/dal/connections/%d/undelete", sl.ID)).
Header("Accept", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
Assert(jsonpath.Equal("$.success.message", "OK")).
End()
}
func Test_dal_crud_connection_undelete_forbidden(t *testing.T) {
h := newHelperT(t)
defer h.cleanupDal()
sl := h.createDalConnection(&types.DalConnection{
Handle: "test_connection",
DeletedAt: &h.cUser.CreatedAt,
DeletedBy: h.cUser.ID,
})
h.apiInit().
Post(fmt.Sprintf("/system/dal/connections/%d/undelete", sl.ID)).
Header("Accept", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertError("dalConnection.errors.notAllowedToUndelete")).
End()
}
+22
View File
@@ -0,0 +1,22 @@
package dal
import (
"net/http"
"testing"
"github.com/cortezaproject/corteza-server/tests/helpers"
jsonpath "github.com/steinfletcher/apitest-jsonpath"
)
func Test_dal_crud_driver_list(t *testing.T) {
h := newHelperT(t)
h.apiInit().
Get("/system/dal/drivers/").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
Assert(jsonpath.GreaterThan("$.response.set", 0)).
Assert(jsonpath.Present("$.response.set[0].capabilities")).
End()
}
@@ -0,0 +1,206 @@
package dal
import (
"encoding/json"
"fmt"
"net/http"
"testing"
"github.com/cortezaproject/corteza-server/compose/types"
systemTypes "github.com/cortezaproject/corteza-server/system/types"
"github.com/cortezaproject/corteza-server/tests/helpers"
jsonpath "github.com/steinfletcher/apitest-jsonpath"
)
func Test_dal_crud_issues_compose_module_missing_sensitivity(t *testing.T) {
h := newHelperT(t)
defer h.cleanupDal()
helpers.AllowMe(h, types.NamespaceRbacResource(0), "module.create")
helpers.AllowMe(h, types.NamespaceRbacResource(0), "modules.search")
helpers.AllowMe(h, types.ModuleRbacResource(0, 0), "read")
helpers.AllowMe(h, types.ModuleRbacResource(0, 0), "update")
helpers.AllowMe(h, types.ModuleRbacResource(0, 0), "delete")
ns := h.createNamespace("test")
aux := &composeModuleRestRsp{}
rsp := h.apiInit().
Post(fmt.Sprintf("/compose/namespace/%d/module/", ns.ID)).
Body(loadRequestFromGenerics(t, "nok_module_missing_sensitivity_level.json")).
Header("Accept", "application/json").
Header("Content-Type", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
Assert(jsonpath.Present("$.response.moduleID")).
Assert(jsonpath.Len("$.response.modelConfig.issues", 1)).
End()
dd := json.NewDecoder(rsp.Response.Body)
h.a.NoError(dd.Decode(&aux))
rsp = h.apiInit().
Post(fmt.Sprintf("/compose/namespace/%d/module/%d", ns.ID, aux.Response.ID)).
Body(loadRequestFromGenerics(t, "ok_module.json")).
Header("Accept", "application/json").
Header("Content-Type", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
Assert(jsonpath.Present("$.response.moduleID")).
Assert(jsonpath.NotPresent("$.response.modelConfig.issues")).
End()
rsp = h.apiInit().
Post(fmt.Sprintf("/compose/namespace/%d/module/%d", ns.ID, aux.Response.ID)).
Body(loadRequestFromGenerics(t, "nok_module_missing_sensitivity_level.json")).
Header("Accept", "application/json").
Header("Content-Type", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
Assert(jsonpath.Present("$.response.moduleID")).
Assert(jsonpath.Len("$.response.modelConfig.issues", 1)).
End()
rsp = h.apiInit().
Delete(fmt.Sprintf("/compose/namespace/%d/module/%d", ns.ID, aux.Response.ID)).
Header("Accept", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
Assert(jsonpath.Present("$.success.message")).
End()
}
func Test_dal_crud_issues_compose_module_field_missing_sensitivity(t *testing.T) {
h := newHelperT(t)
defer h.cleanupDal()
helpers.AllowMe(h, types.NamespaceRbacResource(0), "module.create")
helpers.AllowMe(h, types.NamespaceRbacResource(0), "modules.search")
helpers.AllowMe(h, types.ModuleRbacResource(0, 0), "read")
helpers.AllowMe(h, types.ModuleRbacResource(0, 0), "update")
helpers.AllowMe(h, types.ModuleRbacResource(0, 0), "delete")
ns := h.createNamespace("test")
aux := &composeModuleRestRsp{}
rsp := h.apiInit().
Post(fmt.Sprintf("/compose/namespace/%d/module/", ns.ID)).
Body(loadRequestFromGenerics(t, "nok_module_missing_field_sensitivity_level.json")).
Header("Accept", "application/json").
Header("Content-Type", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
Assert(jsonpath.Present("$.response.moduleID")).
Assert(jsonpath.Len("$.response.modelConfig.issues", 1)).
End()
dd := json.NewDecoder(rsp.Response.Body)
h.a.NoError(dd.Decode(&aux))
rsp = h.apiInit().
Post(fmt.Sprintf("/compose/namespace/%d/module/%d", ns.ID, aux.Response.ID)).
Body(loadRequestFromGenerics(t, "ok_module.json")).
Header("Accept", "application/json").
Header("Content-Type", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
Assert(jsonpath.Present("$.response.moduleID")).
Assert(jsonpath.NotPresent("$.response.modelConfig.issues")).
End()
rsp = h.apiInit().
Post(fmt.Sprintf("/compose/namespace/%d/module/%d", ns.ID, aux.Response.ID)).
Body(loadRequestFromGenerics(t, "nok_module_missing_field_sensitivity_level.json")).
Header("Accept", "application/json").
Header("Content-Type", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
Assert(jsonpath.Present("$.response.moduleID")).
Assert(jsonpath.Len("$.response.modelConfig.issues", 1)).
End()
rsp = h.apiInit().
Delete(fmt.Sprintf("/compose/namespace/%d/module/%d", ns.ID, aux.Response.ID)).
Header("Accept", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
Assert(jsonpath.Present("$.success.message")).
End()
}
func Test_dal_crud_issues_compose_module_nok_connection(t *testing.T) {
h := newHelperT(t)
defer h.cleanupDal()
helpers.AllowMe(h, types.NamespaceRbacResource(0), "module.create")
helpers.AllowMe(h, types.NamespaceRbacResource(0), "modules.search")
helpers.AllowMe(h, types.ModuleRbacResource(0, 0), "read")
helpers.AllowMe(h, types.ModuleRbacResource(0, 0), "update")
helpers.AllowMe(h, types.ModuleRbacResource(0, 0), "delete")
helpers.AllowMe(h, systemTypes.ComponentRbacResource(), "dal-connection.create")
helpers.AllowMe(h, systemTypes.ComponentRbacResource(), "dal-connections.search")
helpers.AllowMe(h, systemTypes.DalConnectionRbacResource(0), "read")
helpers.AllowMe(h, systemTypes.DalConnectionRbacResource(0), "update")
helpers.AllowMe(h, systemTypes.DalConnectionRbacResource(0), "delete")
ns := h.createNamespace("test")
conn := createConnectionFromGenerics(h.secCtx(), t, "nok_connection_connectivity.json")
aux := &composeModuleRestRsp{}
rsp := h.apiInit().
Post(fmt.Sprintf("/compose/namespace/%d/module/", ns.ID)).
Body(loadRequestFromScenarioWithConnection(t, "module.json", conn.ID)).
Header("Accept", "application/json").
Header("Content-Type", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
Assert(jsonpath.Present("$.response.moduleID")).
Assert(jsonpath.Len("$.response.modelConfig.issues", 1)).
End()
dd := json.NewDecoder(rsp.Response.Body)
h.a.NoError(dd.Decode(&aux))
h.apiInit().
Put(fmt.Sprintf("/system/dal/connections/%d", conn.ID)).
Body(loadRequestFromGenerics(t, "ok_connection.json")).
Header("Accept", "application/json").
Header("Content-Type", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
Assert(jsonpath.Present("$.response.connectionID")).
Assert(jsonpath.NotPresent("$.response.issues")).
End()
rsp = h.apiInit().
Post(fmt.Sprintf("/compose/namespace/%d/module/%d", ns.ID, aux.Response.ID)).
Body(loadRequestFromScenarioWithConnection(t, "module.json", conn.ID)).
Header("Accept", "application/json").
Header("Content-Type", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
Assert(jsonpath.Present("$.response.moduleID")).
Assert(jsonpath.NotPresent("$.response.modelConfig.issues")).
End()
rsp = h.apiInit().
Delete(fmt.Sprintf("/compose/namespace/%d/module/%d", ns.ID, aux.Response.ID)).
Header("Accept", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
End()
}
@@ -0,0 +1,126 @@
package dal
import (
"fmt"
"net/http"
"testing"
"github.com/cortezaproject/corteza-server/compose/types"
"github.com/cortezaproject/corteza-server/pkg/dal/capabilities"
systemTypes "github.com/cortezaproject/corteza-server/system/types"
"github.com/cortezaproject/corteza-server/tests/helpers"
)
func Test_dal_crud_issues_compose_record_nok_connection(t *testing.T) {
h := newHelperT(t)
defer h.cleanupDal()
helpers.AllowMe(h, systemTypes.ComponentRbacResource(), "dal-connection.create")
helpers.AllowMe(h, systemTypes.ComponentRbacResource(), "dal-connections.search")
helpers.AllowMe(h, systemTypes.DalConnectionRbacResource(0), "read")
helpers.AllowMe(h, systemTypes.DalConnectionRbacResource(0), "update")
helpers.AllowMe(h, systemTypes.DalConnectionRbacResource(0), "delete")
helpers.AllowMe(h, types.NamespaceRbacResource(0), "module.create")
helpers.AllowMe(h, types.NamespaceRbacResource(0), "modules.search")
helpers.AllowMe(h, types.ModuleRbacResource(0, 0), "read")
helpers.AllowMe(h, types.ModuleRbacResource(0, 0), "update")
helpers.AllowMe(h, types.ModuleRbacResource(0, 0), "delete")
helpers.AllowMe(h, types.ModuleRbacResource(0, 0), "record.create")
helpers.AllowMe(h, types.ModuleRbacResource(0, 0), "records.search")
helpers.AllowMe(h, types.ModuleFieldRbacResource(0, 0, 0), "record.value.update")
helpers.AllowMe(h, types.ModuleFieldRbacResource(0, 0, 0), "record.value.read")
ns := h.createNamespace("test")
connection := createConnectionFromGenerics(h.secCtx(), t, "nok_connection_connectivity.json")
module := createModuleFromGenerics(h.secCtx(), t, "ok_module.json", ns.ID, &types.ModelConfig{
ConnectionID: connection.ID,
Capabilities: capabilities.FullCapabilities(),
})
h.apiInit().
Post(fmt.Sprintf("/compose/namespace/%d/module/%d/record/", ns.ID, module.ID)).
Body(loadRequestFromGenerics(t, "ok_record.json")).
Header("Accept", "application/json").
Header("Content-Type", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertErrorP(fmt.Sprintf("connection %d has issues", connection.ID))).
End()
}
func Test_dal_crud_issues_compose_record_nok_model(t *testing.T) {
h := newHelperT(t)
defer h.cleanupDal()
helpers.AllowMe(h, systemTypes.ComponentRbacResource(), "dal-connection.create")
helpers.AllowMe(h, systemTypes.ComponentRbacResource(), "dal-connections.search")
helpers.AllowMe(h, systemTypes.DalConnectionRbacResource(0), "read")
helpers.AllowMe(h, systemTypes.DalConnectionRbacResource(0), "update")
helpers.AllowMe(h, systemTypes.DalConnectionRbacResource(0), "delete")
helpers.AllowMe(h, types.NamespaceRbacResource(0), "module.create")
helpers.AllowMe(h, types.NamespaceRbacResource(0), "modules.search")
helpers.AllowMe(h, types.ModuleRbacResource(0, 0), "read")
helpers.AllowMe(h, types.ModuleRbacResource(0, 0), "update")
helpers.AllowMe(h, types.ModuleRbacResource(0, 0), "delete")
helpers.AllowMe(h, types.ModuleRbacResource(0, 0), "record.create")
helpers.AllowMe(h, types.ModuleRbacResource(0, 0), "records.search")
helpers.AllowMe(h, types.ModuleFieldRbacResource(0, 0, 0), "record.value.update")
helpers.AllowMe(h, types.ModuleFieldRbacResource(0, 0, 0), "record.value.read")
ns := h.createNamespace("test")
connection := createConnectionFromGenerics(h.secCtx(), t, "ok_connection.json")
module := createModuleFromGenerics(h.secCtx(), t, "nok_module_sensitivity_level.json", ns.ID, &types.ModelConfig{
ConnectionID: connection.ID,
Capabilities: capabilities.FullCapabilities(),
})
h.apiInit().
Post(fmt.Sprintf("/compose/namespace/%d/module/%d/record/", ns.ID, module.ID)).
Body(loadRequestFromGenerics(t, "ok_record.json")).
Header("Accept", "application/json").
Header("Content-Type", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertErrorP(fmt.Sprintf("model %d has issues", module.ID))).
End()
}
func Test_dal_crud_issues_compose_record_ok(t *testing.T) {
h := newHelperT(t)
defer h.cleanupDal()
helpers.AllowMe(h, systemTypes.ComponentRbacResource(), "dal-connection.create")
helpers.AllowMe(h, systemTypes.ComponentRbacResource(), "dal-connections.search")
helpers.AllowMe(h, systemTypes.DalConnectionRbacResource(0), "read")
helpers.AllowMe(h, systemTypes.DalConnectionRbacResource(0), "update")
helpers.AllowMe(h, systemTypes.DalConnectionRbacResource(0), "delete")
helpers.AllowMe(h, types.NamespaceRbacResource(0), "module.create")
helpers.AllowMe(h, types.NamespaceRbacResource(0), "modules.search")
helpers.AllowMe(h, types.ModuleRbacResource(0, 0), "read")
helpers.AllowMe(h, types.ModuleRbacResource(0, 0), "update")
helpers.AllowMe(h, types.ModuleRbacResource(0, 0), "delete")
helpers.AllowMe(h, types.ModuleRbacResource(0, 0), "record.create")
helpers.AllowMe(h, types.ModuleRbacResource(0, 0), "records.search")
helpers.AllowMe(h, types.ModuleFieldRbacResource(0, 0, 0), "record.value.update")
helpers.AllowMe(h, types.ModuleFieldRbacResource(0, 0, 0), "record.value.read")
ns := h.createNamespace("test")
connection := createConnectionFromGenerics(h.secCtx(), t, "ok_connection.json")
module := createModuleFromGenerics(h.secCtx(), t, "ok_module.json", ns.ID, &types.ModelConfig{
ConnectionID: connection.ID,
Capabilities: capabilities.FullCapabilities(),
})
h.apiInit().
Post(fmt.Sprintf("/compose/namespace/%d/module/%d/record/", ns.ID, module.ID)).
Body(loadRequestFromGenerics(t, "ok_record.json")).
Header("Accept", "application/json").
Header("Content-Type", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
End()
}
@@ -0,0 +1,134 @@
package dal
import (
"encoding/json"
"fmt"
"net/http"
"testing"
"github.com/cortezaproject/corteza-server/system/types"
"github.com/cortezaproject/corteza-server/tests/helpers"
jsonpath "github.com/steinfletcher/apitest-jsonpath"
)
func Test_dal_crud_issues_connection_connectivity(t *testing.T) {
h := newHelperT(t)
defer h.cleanupDal()
helpers.AllowMe(h, types.ComponentRbacResource(), "dal-connection.create")
helpers.AllowMe(h, types.ComponentRbacResource(), "dal-connections.search")
helpers.AllowMe(h, types.DalConnectionRbacResource(0), "read")
helpers.AllowMe(h, types.DalConnectionRbacResource(0), "update")
helpers.AllowMe(h, types.DalConnectionRbacResource(0), "delete")
aux := &dalConnectionRestRsp{}
rsp := h.apiInit().
Post("/system/dal/connections/").
Body(loadRequestFromGenerics(t, "nok_connection_connectivity.json")).
Header("Accept", "application/json").
Header("Content-Type", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
Assert(jsonpath.Present("$.response.connectionID")).
Assert(jsonpath.Len("$.response.issues", 1)).
End()
dd := json.NewDecoder(rsp.Response.Body)
h.a.NoError(dd.Decode(&aux))
h.apiInit().
Put(fmt.Sprintf("/system/dal/connections/%d", aux.Response.ID)).
Body(loadRequestFromGenerics(t, "ok_connection.json")).
Header("Accept", "application/json").
Header("Content-Type", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
Assert(jsonpath.Present("$.response.connectionID")).
Assert(jsonpath.NotPresent("$.response.issues")).
End()
h.apiInit().
Put(fmt.Sprintf("/system/dal/connections/%d", aux.Response.ID)).
Body(loadRequestFromGenerics(t, "nok_connection_connectivity.json")).
Header("Accept", "application/json").
Header("Content-Type", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
Assert(jsonpath.Present("$.response.connectionID")).
Assert(jsonpath.Len("$.response.issues", 1)).
End()
h.apiInit().
Delete(fmt.Sprintf("/system/dal/connections/%d", aux.Response.ID)).
Header("Accept", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
Assert(jsonpath.Present("$.success.message")).
End()
}
func Test_dal_crud_issues_connection_sensitivity(t *testing.T) {
h := newHelperT(t)
defer h.cleanupDal()
helpers.AllowMe(h, types.ComponentRbacResource(), "dal-connection.create")
helpers.AllowMe(h, types.ComponentRbacResource(), "dal-connections.search")
helpers.AllowMe(h, types.DalConnectionRbacResource(0), "read")
helpers.AllowMe(h, types.DalConnectionRbacResource(0), "update")
helpers.AllowMe(h, types.DalConnectionRbacResource(0), "delete")
aux := &dalConnectionRestRsp{}
rsp := h.apiInit().
Post("/system/dal/connections/").
Body(loadRequestFromGenerics(t, "nok_connection_sensitivity.json")).
Header("Accept", "application/json").
Header("Content-Type", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
Assert(jsonpath.Present("$.response.connectionID")).
Assert(jsonpath.Len("$.response.issues", 1)).
End()
dd := json.NewDecoder(rsp.Response.Body)
h.a.NoError(dd.Decode(&aux))
h.apiInit().
Put(fmt.Sprintf("/system/dal/connections/%d", aux.Response.ID)).
Body(loadRequestFromGenerics(t, "ok_connection.json")).
Header("Accept", "application/json").
Header("Content-Type", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
Assert(jsonpath.Present("$.response.connectionID")).
Assert(jsonpath.NotPresent("$.response.issues")).
End()
h.apiInit().
Put(fmt.Sprintf("/system/dal/connections/%d", aux.Response.ID)).
Body(loadRequestFromGenerics(t, "nok_connection_sensitivity.json")).
Header("Accept", "application/json").
Header("Content-Type", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
Assert(jsonpath.Present("$.response.connectionID")).
Assert(jsonpath.Len("$.response.issues", 1)).
End()
h.apiInit().
Delete(fmt.Sprintf("/system/dal/connections/%d", aux.Response.ID)).
Header("Accept", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
Assert(jsonpath.Present("$.success.message")).
End()
}
@@ -0,0 +1,229 @@
package dal
import (
"fmt"
"net/http"
"testing"
"github.com/cortezaproject/corteza-server/system/types"
"github.com/cortezaproject/corteza-server/tests/helpers"
jsonpath "github.com/steinfletcher/apitest-jsonpath"
)
func Test_dal_crud_sensitivity_level_list(t *testing.T) {
h := newHelperT(t)
defer h.cleanupDal()
helpers.AllowMe(h, types.ComponentRbacResource(), "dal-sensitivity-level.manage")
h.apiInit().
Get("/system/dal/sensitivity-levels/").
Header("Accept", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
End()
}
func Test_dal_crud_sensitivity_level_list_forbidden(t *testing.T) {
h := newHelperT(t)
defer h.cleanupDal()
h.apiInit().
Get("/system/dal/sensitivity-levels/").
Header("Accept", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertError("dalSensitivityLevel.errors.notAllowedToManage")).
End()
}
func Test_dal_crud_sensitivity_level_create(t *testing.T) {
h := newHelperT(t)
defer h.cleanupDal()
helpers.AllowMe(h, types.ComponentRbacResource(), "dal-sensitivity-level.manage")
h.apiInit().
Post("/system/dal/sensitivity-levels/").
Body(loadRequestFromGenerics(t, "ok_sensitivity_level.json")).
Header("Accept", "application/json").
Header("Content-Type", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
Assert(jsonpath.Present("$.response.sensitivityLevelID")).
End()
}
func Test_dal_crud_sensitivity_level_create_forbidden(t *testing.T) {
h := newHelperT(t)
defer h.cleanupDal()
h.apiInit().
Post("/system/dal/sensitivity-levels/").
Body(loadRequestFromGenerics(t, "ok_sensitivity_level.json")).
Header("Accept", "application/json").
Header("Content-Type", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertError("dalSensitivityLevel.errors.notAllowedToManage")).
End()
}
func Test_dal_crud_sensitivity_level_update(t *testing.T) {
h := newHelperT(t)
defer h.cleanupDal()
sl := h.createSensitivityLevel(&types.DalSensitivityLevel{
Handle: "test_sl",
})
helpers.AllowMe(h, types.ComponentRbacResource(), "dal-sensitivity-level.manage")
h.apiInit().
Put(fmt.Sprintf("/system/dal/sensitivity-levels/%d", sl.ID)).
Header("Accept", "application/json").
Header("Content-Type", "application/json").
Body(loadRequestFromGenerics(t, "ok_sensitivity_level_update.json")).
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
Assert(jsonpath.Present("$.response.sensitivityLevelID")).
Assert(jsonpath.Equal("$.response.handle", "private_edited")).
End()
}
func Test_dal_crud_sensitivity_level_update_forbidden(t *testing.T) {
h := newHelperT(t)
defer h.cleanupDal()
sl := h.createSensitivityLevel(&types.DalSensitivityLevel{
Handle: "test_sl",
})
h.apiInit().
Put(fmt.Sprintf("/system/dal/sensitivity-levels/%d", sl.ID)).
Header("Accept", "application/json").
Header("Content-Type", "application/json").
Body(loadRequestFromGenerics(t, "ok_sensitivity_level_update.json")).
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertError("dalSensitivityLevel.errors.notAllowedToManage")).
End()
}
func Test_dal_crud_sensitivity_level_read(t *testing.T) {
h := newHelperT(t)
defer h.cleanupDal()
sl := h.createSensitivityLevel(&types.DalSensitivityLevel{
Handle: "test_sl",
})
helpers.AllowMe(h, types.ComponentRbacResource(), "dal-sensitivity-level.manage")
h.apiInit().
Get(fmt.Sprintf("/system/dal/sensitivity-levels/%d", sl.ID)).
Header("Accept", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
Assert(jsonpath.Present("$.response.sensitivityLevelID")).
End()
}
func Test_dal_crud_sensitivity_level_read_forbiden(t *testing.T) {
h := newHelperT(t)
defer h.cleanupDal()
sl := h.createSensitivityLevel(&types.DalSensitivityLevel{
Handle: "test_sl",
})
h.apiInit().
Get(fmt.Sprintf("/system/dal/sensitivity-levels/%d", sl.ID)).
Header("Accept", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertError("dalSensitivityLevel.errors.notAllowedToManage")).
End()
}
func Test_dal_crud_sensitivity_level_delete(t *testing.T) {
h := newHelperT(t)
defer h.cleanupDal()
sl := h.createSensitivityLevel(&types.DalSensitivityLevel{
Handle: "test_sl",
})
helpers.AllowMe(h, types.ComponentRbacResource(), "dal-sensitivity-level.manage")
h.apiInit().
Delete(fmt.Sprintf("/system/dal/sensitivity-levels/%d", sl.ID)).
Header("Accept", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
Assert(jsonpath.Equal("$.success.message", "OK")).
End()
}
func Test_dal_crud_sensitivity_level_delete_forbidden(t *testing.T) {
h := newHelperT(t)
defer h.cleanupDal()
sl := h.createSensitivityLevel(&types.DalSensitivityLevel{
Handle: "test_sl",
})
h.apiInit().
Delete(fmt.Sprintf("/system/dal/sensitivity-levels/%d", sl.ID)).
Header("Accept", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertError("dalSensitivityLevel.errors.notAllowedToManage")).
End()
}
func Test_dal_crud_sensitivity_level_undelete(t *testing.T) {
h := newHelperT(t)
defer h.cleanupDal()
sl := h.createSensitivityLevel(&types.DalSensitivityLevel{
Handle: "test_sl",
DeletedAt: &h.cUser.CreatedAt,
DeletedBy: h.cUser.ID,
})
helpers.AllowMe(h, types.ComponentRbacResource(), "dal-sensitivity-level.manage")
h.apiInit().
Post(fmt.Sprintf("/system/dal/sensitivity-levels/%d/undelete", sl.ID)).
Header("Accept", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertNoErrors).
Assert(jsonpath.Equal("$.success.message", "OK")).
End()
}
func Test_dal_crud_sensitivity_level_undelete_forbidden(t *testing.T) {
h := newHelperT(t)
defer h.cleanupDal()
sl := h.createSensitivityLevel(&types.DalSensitivityLevel{
Handle: "test_sl",
DeletedAt: &h.cUser.CreatedAt,
DeletedBy: h.cUser.ID,
})
h.apiInit().
Post(fmt.Sprintf("/system/dal/sensitivity-levels/%d/undelete", sl.ID)).
Header("Accept", "application/json").
Expect(t).
Status(http.StatusOK).
Assert(helpers.AssertError("dalSensitivityLevel.errors.notAllowedToManage")).
End()
}
-84
View File
@@ -1,84 +0,0 @@
package dal
import (
"context"
"github.com/cortezaproject/corteza-server/pkg/dal"
"github.com/cortezaproject/corteza-server/pkg/logger"
"github.com/cortezaproject/corteza-server/tests/dal/setup/mysql"
"github.com/cortezaproject/corteza-server/tests/dal/setup/postgres"
"github.com/cortezaproject/corteza-server/tests/dal/setup/sqlite"
"github.com/cortezaproject/corteza-server/tests/helpers"
_ "github.com/joho/godotenv/autoload"
"os"
"strings"
"testing"
)
// Tests DAL functionalities for all supported databases
//
// Tests for drivers are executed if DSN env variable (DAL_TEST_DSN_<driver>) is present.
// Example: DAL_TEST_DSN_MYSQL, DAL_TEST_DSN_POSTGRES
//
// Multiple space delimited DSNs are supported.
//
// Tests scans current and two parent folders for presence of .env file
// and loads the first one found.
//
//
func TestDAL(t *testing.T) {
helpers.RecursiveDotEnvLoad()
var (
// enrich context with debug logger
//
// this will enable us to log driver commands
// when +debug is used on DSN schema
ctx = logger.ContextWithValue(context.Background(), logger.MakeDebugLogger())
conn dal.Connection
err error
drivers = []struct {
name string
dsn string
connect func(string) (dal.Connection, error)
}{
{
name: "sqlite",
dsn: "sqlite3+debug://file::memory:?cache=shared&mode=memory",
connect: sqlite.Setup,
},
{
name: "mysql",
dsn: os.Getenv("DAL_TEST_DSN_MYSQL"),
connect: mysql.Setup,
},
{
name: "postgres",
dsn: os.Getenv("DAL_TEST_DSN_POSTGRES"),
connect: postgres.Setup,
},
}
)
for _, driver := range drivers {
t.Run(driver.name, func(t *testing.T) {
if driver.dsn == "" {
t.Skip("DSN for DAL test not set")
}
for _, dsn := range strings.Split(driver.dsn, " ") {
t.Run("", func(t *testing.T) {
t.Log("Connecting to ", dsn)
if conn, err = driver.connect(dsn); err != nil {
t.Fatal(err)
}
t.Run("RecordCodec", func(t *testing.T) { RecordCodec(t, ctx, conn) })
t.Run("RecordSearch", func(t *testing.T) { RecordSearch(t, ctx, conn) })
})
}
})
}
}
+375
View File
@@ -0,0 +1,375 @@
package dal
import (
"context"
"fmt"
"os"
"testing"
composeService "github.com/cortezaproject/corteza-server/compose/service"
composeTypes "github.com/cortezaproject/corteza-server/compose/types"
"github.com/cortezaproject/corteza-server/pkg/auth"
"github.com/cortezaproject/corteza-server/pkg/dal"
"github.com/cortezaproject/corteza-server/pkg/dal/capabilities"
"github.com/cortezaproject/corteza-server/pkg/filter"
"github.com/cortezaproject/corteza-server/pkg/id"
"github.com/cortezaproject/corteza-server/store"
"github.com/cortezaproject/corteza-server/system/dalutils"
"github.com/cortezaproject/corteza-server/system/service"
"github.com/cortezaproject/corteza-server/system/types"
"github.com/cortezaproject/corteza-server/tests/dal/setup/mysql"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
)
type (
driver struct {
name string
dsn string
setup func(ctx context.Context, t aaaa, dsn string)
}
dalService interface {
Drivers() (drivers []dal.Driver)
ReloadSensitivityLevels(levels ...dal.SensitivityLevel) (err error)
CreateSensitivityLevel(levels ...dal.SensitivityLevel) (err error)
UpdateSensitivityLevel(levels ...dal.SensitivityLevel) (err error)
DeleteSensitivityLevel(levels ...dal.SensitivityLevel) (err error)
CreateConnection(ctx context.Context, connectionID uint64, cp dal.ConnectionParams, cm dal.ConnectionMeta, capabilities ...capabilities.Capability) (err error)
DeleteConnection(ctx context.Context, connectionID uint64) (err error)
UpdateConnection(ctx context.Context, connectionID uint64, cp dal.ConnectionParams, cm dal.ConnectionMeta, capabilities ...capabilities.Capability) (err error)
Create(ctx context.Context, mf dal.ModelFilter, capabilities capabilities.Set, rr ...dal.ValueGetter) (err error)
Update(ctx context.Context, mf dal.ModelFilter, capabilities capabilities.Set, rr ...dal.ValueGetter) (err error)
Search(ctx context.Context, mf dal.ModelFilter, capabilities capabilities.Set, f filter.Filter) (iter dal.Iterator, err error)
Lookup(ctx context.Context, mf dal.ModelFilter, capabilities capabilities.Set, lookup dal.ValueGetter, dst dal.ValueSetter) (err error)
Delete(ctx context.Context, mf dal.ModelFilter, capabilities capabilities.Set, vv ...dal.ValueGetter) (err error)
Truncate(ctx context.Context, mf dal.ModelFilter, capabilities capabilities.Set) (err error)
ReloadModel(ctx context.Context, models ...*dal.Model) (err error)
SearchModels(ctx context.Context) (out dal.ModelSet, err error)
CreateModel(ctx context.Context, models ...*dal.Model) (err error)
DeleteModel(ctx context.Context, models ...*dal.Model) (err error)
UpdateModel(ctx context.Context, old, new *dal.Model) (err error)
UpdateModelAttribute(ctx context.Context, model *dal.Model, old, new *dal.Attribute, trans ...dal.TransformationFunction) (err error)
ModelIdentFormatter(connectionID uint64) (f *dal.IdentFormatter, err error)
FindModelByResourceID(connectionID uint64, resourceID uint64) *dal.Model
FindModelByResourceIdent(connectionID uint64, resourceType, resourceIdent string) *dal.Model
FindModelByIdent(connectionID uint64, ident string) *dal.Model
SearchConnectionIssues(connectionID uint64) (out []error)
SearchModelIssues(connectionID, resourceID uint64) (out []error)
}
dalConnectionRestRsp struct {
Response *types.DalConnection
}
composeModuleRestRsp struct {
Response *composeTypes.Module
}
composeRecordRestRsp struct {
Response *composeTypes.Record
}
composeRecordSearchRestRsp struct {
Response struct {
Set composeTypes.RecordSet
}
}
aaaa interface {
require.TestingT
Name() string
}
modelMetaMaker func(ident string) *dal.Model
attributeMaker func() dal.AttributeSet
)
const (
sysID = "ID"
sysNamespaceID = "namespaceID"
sysModuleID = "moduleID"
sysCreatedAt = "createdAt"
sysCreatedBy = "createdBy"
sysUpdatedAt = "updatedAt"
sysUpdatedBy = "updatedBy"
sysDeletedAt = "deletedAt"
sysDeletedBy = "deletedBy"
sysOwnedBy = "ownedBy"
colSysID = "id"
colSysNamespaceID = "rel_namespace"
colSysModuleID = "module_id"
colSysCreatedAt = "created_at"
colSysCreatedBy = "created_by"
colSysUpdatedAt = "updated_at"
colSysUpdatedBy = "updated_by"
colSysDeletedAt = "deleted_at"
colSysDeletedBy = "deleted_by"
colSysOwnedBy = "owned_by"
)
func (h helper) cleanupDal() {
ds := service.DefaultStore
ctx := context.Background()
dd := dal.Service()
models, err := dd.SearchModels(ctx)
h.a.NoError(err)
for _, model := range models {
dd.Truncate(ctx, model.ToFilter(), nil)
}
h.a.NoError(store.TruncateComposeModuleFields(ctx, ds))
h.a.NoError(store.TruncateComposeModules(ctx, ds))
h.a.NoError(store.TruncateComposeNamespaces(ctx, ds))
h.a.NoError(store.TruncateDalSensitivityLevels(ctx, ds))
cc, _, err := store.SearchDalConnections(ctx, ds, types.DalConnectionFilter{})
h.a.NoError(err)
for _, c := range cc {
if c.Type == types.DalPrimaryConnectionResourceType {
continue
}
h.a.NoError(store.DeleteDalConnectionByID(ctx, ds, c.ID))
}
h.a.NoError(service.DefaultDalConnection.ReloadConnections(ctx))
h.a.NoError(composeService.DefaultModule.ReloadDALModels(ctx))
}
func initSvc(ctx context.Context, d driver) (dalService, error) {
c := makeConnectionDefinition(d.dsn)
cm, err := dalutils.ConnectionMeta(ctx, c)
if err != nil {
return nil, err
}
svc, err := dal.New(
ctx,
zap.NewNop(),
false,
c.ID,
c.Config.Connection,
cm,
capabilities.FullCapabilities()...,
)
return svc, err
}
func setup(t *testing.T) (ctx context.Context, h helper, log *zap.Logger) {
log = zap.NewNop()
h = newHelperT(t)
u := &types.User{
ID: id.Next(),
}
u.SetRoles(auth.BypassRoles().IDs()...)
ctx = auth.SetIdentityToContext(context.Background(), u)
return ctx, h, log
}
func setupBench(b *testing.B) (ctx context.Context, h helper, log *zap.Logger) {
log = zap.NewNop()
h = newHelperB(b)
u := &types.User{
ID: id.Next(),
}
u.SetRoles(auth.BypassRoles().IDs()...)
ctx = auth.SetIdentityToContext(context.Background(), u)
return ctx, h, log
}
func bootstrap(rootTest *testing.T, run func(context.Context, *testing.T, helper, dalService)) {
var (
ctx, h, log = setup(rootTest)
drivers = collectDrivers()
)
_ = log
for _, driver := range drivers {
rootTest.Run(driver.name, func(t *testing.T) {
if driver.dsn == "" {
t.Skip("DSN for DAL test not set")
}
svc, err := initSvc(ctx, driver)
require.NoError(t, err)
driver.setup(ctx, rootTest, driver.dsn)
run(ctx, t, h, svc)
})
}
}
func bootstrapWithModel(rootTest *testing.T, ident string, run func(context.Context, *testing.T, helper, dalService, *dal.Model)) {
var (
ctx, h, log = setup(rootTest)
drivers = collectDrivers()
)
_ = log
for _, driver := range drivers {
rootTest.Run(driver.name, func(t *testing.T) {
if driver.dsn == "" {
t.Skip("DSN for DAL test not set")
}
svc, err := initSvc(ctx, driver)
require.NoError(t, err)
model := buildModel(ident, basicModelMeta, fullPartitionedSysAttributes)
driver.setup(ctx, rootTest, driver.dsn)
require.NoError(t, svc.CreateModel(ctx, model))
run(ctx, t, h, svc, model)
})
}
}
func bootstrapBenchmark(rootTest *testing.B, run func(context.Context, *testing.B, helper, dalService)) {
var (
ctx, h, log = setupBench(rootTest)
drivers = collectDrivers()
)
_ = log
for _, driver := range drivers {
rootTest.Run(driver.name, func(b *testing.B) {
if driver.dsn == "" {
b.Skip("DSN for DAL test not set")
}
svc, err := initSvc(ctx, driver)
require.NoError(b, err)
driver.setup(ctx, rootTest, driver.dsn)
run(ctx, b, h, svc)
})
}
}
func collectDrivers() []driver {
return []driver{
// {
// name: "sqlite",
// dsn: "sqlite3+debug://file::memory:?cache=shared&mode=memory",
// setup: func(ctx context.Context, t *testing.T, dsn string) {
// conn, err := mysql.Setup(ctx, dsn)
// require.NoError(t, err)
// _, err = conn.Exec(loadSetupSource(t, "sqlite.sql"))
// require.NoError(t, err)
// },
// },
{
name: "mysql",
dsn: os.Getenv("DAL_TEST_DSN_MYSQL"),
setup: func(ctx context.Context, t aaaa, dsn string) {
conn, err := mysql.Setup(ctx, dsn)
require.NoError(t, err)
_, err = conn.Exec(loadScenarioSources(t, "mysql", "sql"))
require.NoError(t, err)
},
},
// {
// name: "postgres",
// dsn: os.Getenv("DAL_TEST_DSN_POSTGRES"),
// setup: func(ctx context.Context, t *testing.T, dsn string) {
// conn, err := mysql.Setup(ctx, dsn)
// require.NoError(t, err)
// _, err = conn.Exec(loadSetupSource(t, "postgres.sql"))
// require.NoError(t, err)
// },
// },
}
}
func buildModel(ident string, mm modelMetaMaker, am attributeMaker, aa ...*dal.Attribute) *dal.Model {
out := mm(ident)
out.Attributes = aa
out.Attributes = append(out.Attributes, am()...)
return out
}
func basicModelMeta(ident string) *dal.Model {
return &dal.Model{
ConnectionID: 0,
Ident: ident,
Resource: fmt.Sprintf("testing-resource/%s", ident),
ResourceID: 0,
}
}
func fullSysAttributes() dal.AttributeSet {
return dal.AttributeSet{
dal.PrimaryAttribute(sysID, &dal.CodecAlias{Ident: colSysID}),
dal.FullAttribute(sysModuleID, &dal.TypeID{}, &dal.CodecAlias{Ident: colSysModuleID}),
dal.FullAttribute(sysNamespaceID, &dal.TypeID{}, &dal.CodecAlias{Ident: colSysNamespaceID}),
dal.FullAttribute(sysOwnedBy, &dal.TypeID{}, &dal.CodecAlias{Ident: colSysOwnedBy}),
dal.FullAttribute(sysCreatedAt, &dal.TypeTimestamp{}, &dal.CodecAlias{Ident: colSysCreatedAt}),
dal.FullAttribute(sysCreatedBy, &dal.TypeID{}, &dal.CodecAlias{Ident: colSysCreatedBy}),
dal.FullAttribute(sysUpdatedAt, &dal.TypeTimestamp{}, &dal.CodecAlias{Ident: colSysUpdatedAt}),
dal.FullAttribute(sysUpdatedBy, &dal.TypeID{}, &dal.CodecAlias{Ident: colSysUpdatedBy}),
dal.FullAttribute(sysDeletedAt, &dal.TypeTimestamp{}, &dal.CodecAlias{Ident: colSysDeletedAt}),
dal.FullAttribute(sysDeletedBy, &dal.TypeID{}, &dal.CodecAlias{Ident: colSysDeletedBy}),
}
}
func fullPartitionedSysAttributes() dal.AttributeSet {
return dal.AttributeSet{
dal.PrimaryAttribute(sysID, &dal.CodecAlias{Ident: colSysID}),
// mod and ns omitted here
dal.FullAttribute(sysOwnedBy, &dal.TypeID{}, &dal.CodecAlias{Ident: colSysOwnedBy}),
dal.FullAttribute(sysCreatedAt, &dal.TypeTimestamp{}, &dal.CodecAlias{Ident: colSysCreatedAt}),
dal.FullAttribute(sysCreatedBy, &dal.TypeID{}, &dal.CodecAlias{Ident: colSysCreatedBy}),
dal.FullAttribute(sysUpdatedAt, &dal.TypeTimestamp{}, &dal.CodecAlias{Ident: colSysUpdatedAt}),
dal.FullAttribute(sysUpdatedBy, &dal.TypeID{}, &dal.CodecAlias{Ident: colSysUpdatedBy}),
dal.FullAttribute(sysDeletedAt, &dal.TypeTimestamp{}, &dal.CodecAlias{Ident: colSysDeletedAt}),
dal.FullAttribute(sysDeletedBy, &dal.TypeID{}, &dal.CodecAlias{Ident: colSysDeletedBy}),
}
}
func drain(ctx context.Context, i dal.Iterator) (rr []*composeTypes.Record, err error) {
var r *composeTypes.Record
rr = make([]*composeTypes.Record, 0, 100)
for i.Next(ctx) {
if i.Err() != nil {
return nil, i.Err()
}
r = new(composeTypes.Record)
if err = i.Scan(r); err != nil {
return
}
rr = append(rr, r)
}
return rr, i.Err()
}
+237
View File
@@ -0,0 +1,237 @@
package dal
import (
"context"
"testing"
"time"
composeTypes "github.com/cortezaproject/corteza-server/compose/types"
"github.com/cortezaproject/corteza-server/pkg/auth"
"github.com/cortezaproject/corteza-server/pkg/dal"
"github.com/cortezaproject/corteza-server/pkg/dal/capabilities"
"github.com/cortezaproject/corteza-server/pkg/id"
"github.com/cortezaproject/corteza-server/pkg/rbac"
"github.com/cortezaproject/corteza-server/store"
"github.com/cortezaproject/corteza-server/system/service"
"github.com/cortezaproject/corteza-server/system/types"
"github.com/cortezaproject/corteza-server/tests/helpers"
"github.com/steinfletcher/apitest"
"github.com/stretchr/testify/require"
)
type (
helper struct {
t *testing.T
b *testing.B
a *require.Assertions
cUser *types.User
roleID uint64
token []byte
}
)
var (
testUser *types.User
)
func newHelperT(t *testing.T) helper {
h := newHelper(t, require.New(t))
h.t = t
return h
}
func newHelperB(b *testing.B) helper {
h := newHelper(b, require.New(b))
h.b = b
return h
}
func newHelper(t require.TestingT, a *require.Assertions) helper {
var (
h = helper{
roleID: id.Next(),
a: a,
}
ctx = context.Background()
err error
)
if testUser == nil {
testUser = &types.User{
Handle: "test_user",
Name: "test_user",
ID: id.Next(),
}
err = store.CreateUser(ctx, service.DefaultStore, testUser)
if err != nil {
panic(err)
}
}
h.cUser = testUser
h.cUser.SetRoles(h.roleID)
helpers.UpdateRBAC(h.roleID)
h.mockPermissionsWithAccess()
h.token, err = auth.TokenIssuer.Issue(ctx, auth.WithIdentity(h.cUser))
if err != nil {
panic(err)
}
return h
}
// apitest basics, initialize, set handler, add auth
func (h helper) apiInit() *apitest.APITest {
InitTestApp()
return apitest.
New().
Handler(r).
Intercept(helpers.ReqHeaderRawAuthBearer(h.token))
}
func (h helper) MyRole() uint64 {
return h.roleID
}
// Returns context w/ security details
func (h helper) secCtx() context.Context {
return auth.SetIdentityToContext(context.Background(), h.cUser)
}
func (h helper) mockPermissions(rules ...*rbac.Rule) {
h.a.NoError(rbac.Global().Grant(
// TestService we use does not have any backend storage,
context.Background(),
rules...,
))
}
// Prepends allow access rule for system service for everyone
func (h helper) mockPermissionsWithAccess(rules ...*rbac.Rule) {
h.mockPermissions(rules...)
}
// // // // // // // // // // // // // // // // // // // // // // // // //
// Resource utilities
func (h helper) createNamespace(name string) *composeTypes.Namespace {
ns := &composeTypes.Namespace{Name: name, Slug: name}
ns.ID = id.Next()
ns.CreatedAt = time.Now()
h.a.NoError(store.CreateComposeNamespace(context.Background(), service.DefaultStore, ns))
return ns
}
func (h helper) createSensitivityLevel(res *types.DalSensitivityLevel) *types.DalSensitivityLevel {
if res.ID == 0 {
res.ID = id.Next()
}
if res.CreatedAt.IsZero() {
res.CreatedAt = time.Now()
}
h.a.NoError(service.DefaultStore.CreateDalSensitivityLevel(context.Background(), res))
return res
}
func (h helper) createDalConnection(res *types.DalConnection) *types.DalConnection {
if res.ID == 0 {
res.ID = id.Next()
}
if res.Name == "" {
res.Name = "Test Connection"
}
if res.Handle == "" {
res.Handle = "test_connection"
}
if res.Type == "" {
res.Type = types.DalConnectionResourceType
}
if res.Ownership == "" {
res.Ownership = "tester"
}
if res.Config.DefaultModelIdent == "" {
res.Config.DefaultModelIdent = "compose_records"
}
if res.Config.DefaultAttributeIdent == "" {
res.Config.DefaultAttributeIdent = "values"
}
if res.Config.DefaultPartitionFormat == "" {
res.Config.DefaultPartitionFormat = "compose_records_{{namespace}}_{{module}}"
}
if res.Config.PartitionFormatValidator == "" {
res.Config.PartitionFormatValidator = ""
}
if res.Config.Connection.Params == nil {
res.Config.Connection = dal.NewDSNConnection("sqlite3://file::memory:?cache=shared&mode=memory")
}
if len(res.Capabilities.Enforced) == 0 {
res.Capabilities.Enforced = capabilities.FullCapabilities()
}
if len(res.Capabilities.Supported) == 0 {
res.Capabilities.Supported = capabilities.Set{}
}
if len(res.Capabilities.Unsupported) == 0 {
res.Capabilities.Unsupported = capabilities.Set{}
}
if len(res.Capabilities.Enabled) == 0 {
res.Capabilities.Enabled = capabilities.Set{}
}
if res.CreatedAt.IsZero() {
res.CreatedAt = time.Now()
}
if res.CreatedBy == 0 {
res.CreatedBy = h.cUser.ID
}
h.a.NoError(service.DefaultStore.CreateDalConnection(context.Background(), res))
h.a.NoError(service.DefaultDalConnection.ReloadConnections(context.Background()))
return res
}
func (h helper) getPrimaryConnection() *types.DalConnection {
cc, _, err := store.SearchDalConnections(context.Background(), service.DefaultStore, types.DalConnectionFilter{Type: types.DalPrimaryConnectionResourceType})
h.a.NoError(err)
if len(cc) != 1 {
h.a.FailNow("invalid state: no or too many primary connections")
}
return cc[0]
}
func makeConnectionDefinition(dsn string) *types.DalConnection {
return &types.DalConnection{
ID: id.Next(),
Type: types.DalConnectionResourceType,
Config: types.ConnectionConfig{
DefaultModelIdent: "compose_record",
DefaultAttributeIdent: "values",
DefaultPartitionFormat: "compose_record_{{namespace}}_{{module}}",
PartitionFormatValidator: "",
Connection: dal.NewDSNConnection(dsn),
},
Capabilities: types.ConnectionCapabilities{
Supported: capabilities.FullCapabilities(),
},
}
}
// // // // // // // // // // // // // // // // // // // // // // // // //
-26
View File
@@ -1,26 +0,0 @@
package dal
import (
"context"
"github.com/cortezaproject/corteza-server/compose/types"
"github.com/cortezaproject/corteza-server/pkg/dal"
)
func drain(ctx context.Context, i dal.Iterator) (rr []*types.Record, err error) {
var r *types.Record
rr = make([]*types.Record, 0, 100)
for i.Next(ctx) {
if i.Err() != nil {
return nil, i.Err()
}
r = new(types.Record)
if err = i.Scan(r); err != nil {
return
}
rr = append(rr, r)
}
return rr, i.Err()
}
+241
View File
@@ -0,0 +1,241 @@
package dal
import (
"bufio"
"context"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path"
"testing"
"github.com/cortezaproject/corteza-server/app"
"github.com/cortezaproject/corteza-server/auth/handlers"
"github.com/cortezaproject/corteza-server/auth/request"
composeRest "github.com/cortezaproject/corteza-server/compose/rest"
composeService "github.com/cortezaproject/corteza-server/compose/service"
composeTypes "github.com/cortezaproject/corteza-server/compose/types"
"github.com/cortezaproject/corteza-server/pkg/api/server"
"github.com/cortezaproject/corteza-server/pkg/cli"
"github.com/cortezaproject/corteza-server/pkg/locale"
"github.com/cortezaproject/corteza-server/pkg/logger"
"github.com/cortezaproject/corteza-server/pkg/objstore/plain"
"github.com/cortezaproject/corteza-server/pkg/rand"
"github.com/cortezaproject/corteza-server/store/adapters/rdbms/drivers/sqlite"
"github.com/cortezaproject/corteza-server/system/rest"
"github.com/cortezaproject/corteza-server/system/service"
"github.com/cortezaproject/corteza-server/system/types"
"github.com/cortezaproject/corteza-server/tests/helpers"
"github.com/go-chi/chi/v5"
_ "github.com/joho/godotenv/autoload"
"github.com/spf13/afero"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"golang.org/x/text/language"
)
var (
testApp *app.CortezaApp
r chi.Router
hh *handlers.AuthHandlers
)
func init() {
helpers.RecursiveDotEnvLoad()
}
func TestMain(m *testing.M) {
InitTestApp()
os.Exit(m.Run())
}
func InitTestApp() {
var sm *request.SessionManager
if testApp == nil {
ctx := cli.Context()
testApp = helpers.NewIntegrationTestApp(ctx, func(app *app.CortezaApp) (err error) {
service.CurrentSettings.Auth.External.Enabled = true
service.DefaultObjectStore, err = plain.NewWithAfero(afero.NewMemMapFs(), "test")
if err != nil {
return err
}
service.DefaultStore, err = sqlite.ConnectInMemory(ctx)
if err != nil {
return err
}
// Tests should be executed w/o any locales
locale.SetGlobal(locale.Static(&locale.Language{Tag: language.Und}))
sm = request.NewSessionManager(service.DefaultStore, app.Opt.Auth, service.DefaultLogger)
return nil
})
}
hh = &handlers.AuthHandlers{
Log: zap.NewNop(),
AuthService: service.DefaultAuth,
SessionManager: sm,
}
if r == nil {
r = chi.NewRouter()
r.Use(server.BaseMiddleware(false, logger.Default())...)
helpers.BindAuthMiddleware(r)
r.Route("/system", func(r chi.Router) {
r.Group(rest.MountRoutes())
})
r.Route("/compose", func(r chi.Router) {
r.Group(composeRest.MountRoutes())
})
hh.MountHttpRoutes(r)
}
}
// // // // // // // // // // // // // // // // // // // // // // // // //
// Utilities
// ---
func loadRequestFromScenario(t *testing.T, name string) string {
return loadRequestFrom(t, suiteFromT(t), name)
}
func loadRequestFromGenerics(t *testing.T, name string) string {
return loadRequestFrom(t, "generic", name)
}
func loadRequestFrom(t *testing.T, suite, name string) string {
f, err := os.Open(path.Join("testdata", suite, name))
require.NoError(t, err)
defer f.Close()
bb, err := ioutil.ReadAll(f)
require.NoError(t, err)
return string(bb)
}
func loadRequestFromScenarioWithConnection(t *testing.T, req string, connectionID uint64) string {
out := loadRequestFromScenario(t, req)
aux := &composeTypes.Module{}
require.NoError(t, json.Unmarshal([]byte(out), &aux))
aux.ModelConfig.ConnectionID = connectionID
a, err := json.Marshal(aux)
require.NoError(t, err)
return string(a)
}
func createRecordFromCase(ctx context.Context, t *testing.T, name string, namespaceID, moduleID uint64) (record *composeTypes.Record) {
return createRecordFrom(ctx, t, suiteFromT(t), name, namespaceID, moduleID)
}
func createRecordFromGenerics(ctx context.Context, t *testing.T, name string, namespaceID, moduleID uint64) (record *composeTypes.Record) {
return createRecordFrom(ctx, t, "generic", name, namespaceID, moduleID)
}
func createRecordFrom(ctx context.Context, t *testing.T, suite, name string, namespaceID, moduleID uint64) (record *composeTypes.Record) {
raw := loadRequestFrom(t, suite, name)
record = &composeTypes.Record{}
require.NoError(t, json.Unmarshal([]byte(raw), &record))
record.NamespaceID = namespaceID
record.ModuleID = moduleID
record, err := composeService.DefaultRecord.Create(ctx, record)
require.NoError(t, err)
return record
}
func createModuleFromCase(ctx context.Context, t *testing.T, name string, namespaceID uint64, config *composeTypes.ModelConfig) (module *composeTypes.Module) {
return createModuleFrom(ctx, t, suiteFromT(t), name, namespaceID, config)
}
func createModuleFromGenerics(ctx context.Context, t *testing.T, name string, namespaceID uint64, config *composeTypes.ModelConfig) (module *composeTypes.Module) {
return createModuleFrom(ctx, t, "generic", name, namespaceID, config)
}
func createModuleFrom(ctx context.Context, t *testing.T, suite, name string, namespaceID uint64, config *composeTypes.ModelConfig) (module *composeTypes.Module) {
raw := loadRequestFrom(t, suite, name)
module = &composeTypes.Module{}
require.NoError(t, json.Unmarshal([]byte(raw), &module))
module.NamespaceID = namespaceID
if config != nil {
module.ModelConfig = *config
}
module, err := composeService.DefaultModule.Create(ctx, module)
require.NoError(t, err)
return module
}
func createConnectionFromCase(ctx context.Context, t *testing.T, name string) (connection *types.DalConnection) {
return createConnectionFrom(ctx, t, suiteFromT(t), name)
}
func createConnectionFromGenerics(ctx context.Context, t *testing.T, name string) (connection *types.DalConnection) {
return createConnectionFrom(ctx, t, "generic", name)
}
func createConnectionFrom(ctx context.Context, t *testing.T, suite, name string) (connection *types.DalConnection) {
raw := loadRequestFrom(t, suite, name)
aux := &types.DalConnection{}
require.NoError(t, json.Unmarshal([]byte(raw), &aux))
connection, err := service.DefaultDalConnection.Create(ctx, aux)
require.NoError(t, err)
return connection
}
// ---
func suiteFromT(t *testing.T) string {
return t.Name()[5:]
}
// random string, 10 chars long by default
func rs(a ...int) string {
var l = 10
if len(a) > 0 {
l = a[0]
}
return string(rand.Bytes(l))
}
func loadScenarioSources(t aaaa, driver, ext string) (src string) {
scenarioName := t.Name()[5:]
src = loadScenarioSource(t, "generic", fmt.Sprintf("_.%s", ext))
src += "\n"
src += loadScenarioSource(t, "generic", fmt.Sprintf("%s.%s", driver, ext))
src += "\n"
src += loadScenarioSource(t, scenarioName, fmt.Sprintf("_.%s", ext))
src += "\n"
src += loadScenarioSource(t, scenarioName, fmt.Sprintf("%s.%s", driver, ext))
return
}
func loadScenarioSource(t aaaa, scenarioName, srcName string) (src string) {
f, err := os.Open(path.Join("testdata", scenarioName, srcName))
if err != nil && os.IsNotExist(err) {
return ""
}
require.NoError(t, err)
defer f.Close()
out, err := ioutil.ReadAll(bufio.NewReader(f))
require.NoError(t, err)
return string(out)
}
-184
View File
@@ -1,184 +0,0 @@
package dal
import (
"context"
"fmt"
"strconv"
"testing"
"time"
"github.com/cortezaproject/corteza-server/compose/types"
"github.com/cortezaproject/corteza-server/pkg/dal"
"github.com/cortezaproject/corteza-server/pkg/filter"
"github.com/stretchr/testify/require"
)
func RecordSearch(t *testing.T, ctx context.Context, d dal.Connection) {
const (
totalRecords = 10
)
var (
req = require.New(t)
m = &dal.Model{
Ident: "crs_test_search",
Attributes: dal.AttributeSet{
&dal.Attribute{Ident: "ID", Type: &dal.TypeID{}, Store: &dal.CodecAlias{Ident: "id"}, PrimaryKey: true},
&dal.Attribute{Ident: "createdAt", Type: &dal.TypeTimestamp{}, Store: &dal.CodecAlias{Ident: "created_at"}},
&dal.Attribute{Ident: "updatedAt", Type: &dal.TypeTimestamp{}, Store: &dal.CodecAlias{Ident: "updated_at"}},
&dal.Attribute{Ident: "v_string", Filterable: true, Type: &dal.TypeText{}, Store: &dal.CodecRecordValueSetJSON{Ident: "meta"}},
&dal.Attribute{Ident: "v_number", Filterable: true, Type: &dal.TypeNumber{}, Store: &dal.CodecRecordValueSetJSON{Ident: "meta"}},
&dal.Attribute{Ident: "v_is_odd", Filterable: true, Type: &dal.TypeBoolean{}, Store: &dal.CodecRecordValueSetJSON{Ident: "meta"}},
&dal.Attribute{Ident: "p_string", Filterable: true, Type: &dal.TypeText{}, Store: &dal.CodecPlain{}},
&dal.Attribute{Ident: "p_number", Filterable: true, Type: &dal.TypeNumber{}, Store: &dal.CodecPlain{}},
&dal.Attribute{Ident: "p_is_odd", Filterable: true, Type: &dal.TypeBoolean{}, Store: &dal.CodecPlain{}},
},
}
)
for ID := uint64(1); ID <= totalRecords; ID++ {
r := &types.Record{ID: ID, CreatedAt: time.Now()}
i := int(ID)
r.Values = r.Values.Set(&types.RecordValue{Name: "v_string", Value: "tens_" + strconv.Itoa(i%10)})
r.Values = r.Values.Set(&types.RecordValue{Name: "v_number", Value: strconv.Itoa(i)})
r.Values = r.Values.Set(&types.RecordValue{Name: "v_is_odd", Value: strconv.FormatBool(i%2 == 1)})
r.Values = r.Values.Set(&types.RecordValue{Name: "p_string", Value: "tens_" + strconv.Itoa(i%10)})
r.Values = r.Values.Set(&types.RecordValue{Name: "p_number", Value: strconv.Itoa(i)})
r.Values = r.Values.Set(&types.RecordValue{Name: "p_is_odd", Value: strconv.FormatBool(i%2 == 1)})
req.NoError(d.Create(ctx, m, r))
}
cases := []struct {
f types.RecordFilter
total int
}{
{
total: totalRecords,
},
{
f: types.RecordFilter{Query: "v_string == p_string"},
total: totalRecords,
},
{
f: types.RecordFilter{Query: "v_number == p_number"},
total: totalRecords,
},
{
f: types.RecordFilter{Query: "p_is_odd"},
total: totalRecords / 2,
},
{
f: types.RecordFilter{Query: "true = p_is_odd"},
total: totalRecords / 2,
},
{
f: types.RecordFilter{Query: "p_is_odd = true"},
total: totalRecords / 2,
},
{
f: types.RecordFilter{Query: "!p_is_odd"},
total: totalRecords / 2,
},
{
f: types.RecordFilter{Query: "p_number = 1"},
total: 1,
},
}
for _, c := range cases {
t.Run(c.f.Query, func(t *testing.T) {
var (
req = require.New(t)
)
i, err := d.Search(ctx, m, c.f.ToFilter())
req.NoError(err)
rr, err := drain(ctx, i)
req.NoError(err)
req.Len(rr, c.total)
})
}
t.Run("paging", func(t *testing.T) {
var (
req = require.New(t)
ids string
fwd, bck *filter.PagingCursor
search = func(where, orderBy string, lim uint, cur *filter.PagingCursor) (ids string, fwd, bck *filter.PagingCursor) {
f := types.RecordFilter{Query: where}
f.PageCursor = cur
f.Limit = lim
req.NoError(f.Sort.Set(orderBy))
i, err := d.Search(ctx, m, f.ToFilter())
req.NoError(err)
req.NoError(i.Err())
if !i.Next(ctx) {
req.NoError(i.Err())
return
}
r := new(types.Record)
req.NoError(i.Scan(r))
bck, err = i.BackCursor(r)
req.NoError(err)
t.Logf("bck-cursor (from the 1st fetched record): %v", bck)
rr, err := drain(ctx, i)
req.NoError(err)
if len(rr) > 0 {
fwd, err = i.ForwardCursor(rr[len(rr)-1])
req.NoError(err)
t.Logf("fwd-cursor (from the lst fetched record): %v", fwd)
}
ids = fmt.Sprintf("%d", r.ID)
for _, r = range rr {
ids += fmt.Sprintf(",%d", r.ID)
}
return
}
)
ids, fwd, _ = search("", "", 3, nil)
req.Equal("1,2,3", ids)
ids, fwd, _ = search("", "", 3, fwd)
req.Equal("4,5,6", ids)
ids, fwd, _ = search("", "", 3, fwd)
req.Equal("7,8,9", ids)
ids, _, bck = search("", "", 3, fwd)
req.Equal("10", ids)
ids, _, bck = search("", "", 3, bck)
req.Equal("7,8,9", ids)
ids, _, bck = search("", "", 3, bck)
req.Equal("4,5,6", ids)
ids, _, bck = search("", "", 3, bck)
req.Equal("1,2,3", ids)
ids, fwd, _ = search("", "p_is_odd", 3, nil)
req.Equal("2,4,6", ids)
ids, _, bck = search("", "", 3, fwd)
req.Equal("8,10,1", ids)
ids, _, _ = search("", "", 3, bck)
req.Equal("2,4,6", ids)
ids, fwd, _ = search("", "v_is_odd", 3, nil)
req.Equal("2,4,6", ids)
ids, _, bck = search("", "", 3, fwd)
req.Equal("8,10,1", ids)
ids, _, _ = search("", "", 3, bck)
req.Equal("2,4,6", ids)
_, _ = fwd, bck // avoiding unused var. error
})
}
+3 -64
View File
@@ -2,21 +2,16 @@ package mysql
import (
"context"
"github.com/cortezaproject/corteza-server/pkg/dal"
"github.com/cortezaproject/corteza-server/pkg/logger"
"github.com/cortezaproject/corteza-server/store/adapters/rdbms"
rdbmsDAL "github.com/cortezaproject/corteza-server/store/adapters/rdbms/dal"
"github.com/cortezaproject/corteza-server/store/adapters/rdbms/ddl"
"github.com/cortezaproject/corteza-server/store/adapters/rdbms/drivers/mysql"
"github.com/jmoiron/sqlx"
"strings"
)
func Setup(dsn string) (_ dal.Connection, err error) {
func Setup(ctx context.Context, dsn string) (_ *sqlx.DB, err error) {
var (
ctx = logger.ContextWithValue(context.Background(), logger.MakeDebugLogger())
cfg *rdbms.ConnConfig
db *sqlx.DB
)
cfg, err = mysql.NewConfig(dsn)
@@ -24,61 +19,5 @@ func Setup(dsn string) (_ dal.Connection, err error) {
return
}
db, err = rdbms.Connect(ctx, logger.MakeDebugLogger(), cfg)
if err != nil {
return
}
if err = tables(ctx, db); err != nil {
return
}
return rdbmsDAL.Connection(db, mysql.Dialect()), nil
}
// remove when store support for table creation is added to CRS
//
// When support for creating DDL commands (creating tables) from DAL models and attributes
// is added, this can be removed!
func tables(ctx context.Context, db sqlx.ExecerContext) (err error) {
return ddl.Exec(ctx, db,
`DROP TABLE IF EXISTS crs_test_codec`,
strings.ReplaceAll(
`CREATE TABLE IF NOT EXISTS crs_test_codec (
"id" BIGINT UNSIGNED,
"created_at" DATETIME,
"updated_at" DATETIME,
"meta" JSON,
"pID" BIGINT UNSIGNED,
"pRef" BIGINT UNSIGNED,
"pTimestamp_TZT" DATETIME,
"pTimestamp_TZF" DATETIME,
"pTime" TIME,
"pDate" DATE,
"pNumber" NUMERIC,
"pText" TEXT,
"pBoolean_T" BOOLEAN,
"pBoolean_F" BOOLEAN,
"pEnum" TEXT,
"pGeometry" TEXT,
"pJSON" TEXT,
"pBlob" BLOB,
"pUUID" VARCHAR(36),
PRIMARY KEY("id")
)`, "\"", "`"),
`DROP TABLE IF EXISTS crs_test_search`,
strings.ReplaceAll(
`CREATE TABLE IF NOT EXISTS crs_test_search (
"id" INT NOT NULL,
"created_at" DATETIME,
"updated_at" DATETIME,
"meta" JSON,
"p_string" TEXT,
"p_number" NUMERIC,
"p_is_odd" BOOLEAN,
PRIMARY KEY("id")
)`, "\"", "`"),
)
return rdbms.Connect(ctx, logger.MakeDebugLogger(), cfg)
}
+3 -60
View File
@@ -2,20 +2,16 @@ package postgres
import (
"context"
"github.com/cortezaproject/corteza-server/pkg/dal"
"github.com/cortezaproject/corteza-server/pkg/logger"
"github.com/cortezaproject/corteza-server/store/adapters/rdbms"
rdbmsDAL "github.com/cortezaproject/corteza-server/store/adapters/rdbms/dal"
"github.com/cortezaproject/corteza-server/store/adapters/rdbms/ddl"
"github.com/cortezaproject/corteza-server/store/adapters/rdbms/drivers/postgres"
"github.com/jmoiron/sqlx"
)
func Setup(dsn string) (_ dal.Connection, err error) {
func Setup(ctx context.Context, dsn string) (_ *sqlx.DB, err error) {
var (
ctx = logger.ContextWithValue(context.Background(), logger.MakeDebugLogger())
cfg *rdbms.ConnConfig
db *sqlx.DB
)
cfg, err = postgres.NewConfig(dsn)
@@ -23,58 +19,5 @@ func Setup(dsn string) (_ dal.Connection, err error) {
return
}
db, err = rdbms.Connect(ctx, logger.MakeDebugLogger(), cfg)
if err != nil {
return
}
if err = tables(ctx, db); err != nil {
return
}
return rdbmsDAL.Connection(db, postgres.Dialect()), nil
}
// remove when store support for table creation is added to CRS
//
// When support for creating DDL commands (creating tables) from DAL models and attributes
// is added, this can be removed!
func tables(ctx context.Context, db sqlx.ExecerContext) (err error) {
return ddl.Exec(ctx, db,
`CREATE TEMPORARY TABLE IF NOT EXISTS crs_test_codec (
"id" BIGINT NOT NULL,
"created_at" TIMESTAMP NOT NULL,
"updated_at" TIMESTAMP,
"meta" JSON,
"pID" BIGINT,
"pRef" BIGINT,
"pTimestamp_TZT" TIMESTAMPTZ,
"pTimestamp_TZF" TIMESTAMP,
"pTime" TIME,
"pDate" DATE,
"pNumber" NUMERIC,
"pText" TEXT,
"pBoolean_T" BOOLEAN,
"pBoolean_F" BOOLEAN,
"pEnum" TEXT,
"pGeometry" TEXT,
"pJSON" TEXT,
"pBlob" BYTEA,
"pUUID" UUID,
PRIMARY KEY(id)
)`,
`CREATE TEMPORARY TABLE IF NOT EXISTS crs_test_search (
"id" BIGINT NOT NULL,
"created_at" TIMESTAMPTZ NOT NULL,
"updated_at" TIMESTAMPTZ,
"meta" JSON,
"p_string" TEXT,
"p_number" NUMERIC,
"p_is_odd" BOOLEAN,
PRIMARY KEY(id )
)`,
)
return rdbms.Connect(ctx, logger.MakeDebugLogger(), cfg)
}
+3 -62
View File
@@ -2,20 +2,16 @@ package sqlite
import (
"context"
"github.com/cortezaproject/corteza-server/pkg/dal"
"github.com/cortezaproject/corteza-server/pkg/logger"
"github.com/cortezaproject/corteza-server/store/adapters/rdbms"
rdbmsDAL "github.com/cortezaproject/corteza-server/store/adapters/rdbms/dal"
"github.com/cortezaproject/corteza-server/store/adapters/rdbms/ddl"
"github.com/cortezaproject/corteza-server/store/adapters/rdbms/drivers/sqlite"
"github.com/jmoiron/sqlx"
)
func Setup(dsn string) (_ dal.Connection, err error) {
func Setup(ctx context.Context, dsn string) (_ *sqlx.DB, err error) {
var (
ctx = logger.ContextWithValue(context.Background(), logger.MakeDebugLogger())
cfg *rdbms.ConnConfig
db *sqlx.DB
)
cfg, err = sqlite.NewConfig(dsn)
@@ -23,60 +19,5 @@ func Setup(dsn string) (_ dal.Connection, err error) {
return
}
db, err = rdbms.Connect(ctx, logger.MakeDebugLogger(), cfg)
if err != nil {
return
}
if err = tables(ctx, db); err != nil {
return
}
return rdbmsDAL.Connection(db, sqlite.Dialect()), nil
}
// remove when store support for table creation is added to CRS
//
// When support for creating DDL commands (creating tables) from DAL models and attributes
// is added, this can be removed!
func tables(ctx context.Context, db sqlx.ExecerContext) (err error) {
return ddl.Exec(ctx, db,
`DROP TABLE IF EXISTS crs_test_codec`,
`CREATE TABLE IF NOT EXISTS crs_test_codec (
id UNSIGNED BIG INT NOT NULL,
created_at TIMESTAMP,
updated_at TIMESTAMP,
meta JSON,
pID UNSIGNED BIG INT,
pRef UNSIGNED BIG INT,
pTimestamp_TZT TIMESTAMP,
pTimestamp_TZF TIMESTAMP,
pTime TIME,
pDate DATE,
pNumber NUMERIC,
pText TEXT,
pBoolean_T BOOLEAN,
pBoolean_F BOOLEAN,
pEnum TEXT,
pGeometry TEXT,
pJSON TEXT,
pBlob BLOB,
pUUID UUID,
PRIMARY KEY(id)
)`,
`DROP TABLE IF EXISTS crs_test_search`,
`CREATE TABLE IF NOT EXISTS crs_test_search (
id INT NOT NULL,
created_at TIMESTAMP,
updated_at TIMESTAMP,
meta JSON,
p_string TEXT,
p_number NUMERIC,
p_is_odd BOOLEAN,
PRIMARY KEY(id )
)`,
)
return rdbms.Connect(ctx, logger.MakeDebugLogger(), cfg)
}
@@ -0,0 +1,43 @@
{
"name": "Primary Connection EDITED",
"handle": "primary_connection",
"type": "corteza::system:primary_dal_connection",
"location": {
"geometry": {
"type": "",
"coordinates": null
},
"properties": {
"name": ""
}
},
"ownership": "",
"sensitivityLevel": "0",
"config": {
"defaultModelIdent": "compose_record",
"defaultAttributeIdent": "values",
"defaultPartitionFormat": "compose_record_{{namespace}}_{{module}}",
"partitionFormatValidator": "",
"connection": {
"type": "corteza::dal:connection:dsn",
"params": {
"dsn": "sqlite3://file::memory:?cache=shared&mode=memory"
}
}
},
"capabilities": {
"enforced": [],
"supported": [
"corteza::dal:capability:create",
"corteza::dal:capability:update",
"corteza::dal:capability:search",
"corteza::dal:capability:lookup",
"corteza::dal:capability:paging",
"corteza::dal:capability:stats",
"corteza::dal:capability:sorting",
"corteza::dal:capability:RBAC"
],
"unsupported": [],
"enabled": []
}
}
@@ -0,0 +1,31 @@
{
"name": "internal_non_partitioned",
"handle": "internal_non_partitioned",
"modelConfig": {
"connectionID": "42",
"capabilities": [],
"partitioned": false
},
"privacy": {
"sensitivityLevel": "0",
"usageDisclosure": "A"
},
"fields": [
{
"name": "f1",
"kind": "String",
"privacy": {
"sensitivityLevel": "0",
"usageDisclosure": "A"
}
},
{
"name": "f2",
"kind": "String"
}
]
}
+48
View File
@@ -0,0 +1,48 @@
DROP TABLE IF EXISTS compose_record;
CREATE TABLE `compose_record` (
`id` bigint unsigned NOT NULL,
`rel_namespace` bigint unsigned NOT NULL,
`module_id` bigint unsigned NOT NULL,
`values` json DEFAULT NULL,
`owned_by` bigint unsigned NOT NULL,
`created_at` datetime NOT NULL,
`updated_at` datetime DEFAULT NULL,
`deleted_at` datetime DEFAULT NULL,
`created_by` bigint unsigned NOT NULL,
`updated_by` bigint unsigned NOT NULL DEFAULT '0',
`deleted_by` bigint unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `compose_record_owner` (`owned_by`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
DROP TABLE IF EXISTS compose_record_partitioned;
CREATE TABLE `compose_record_partitioned` (
`id` bigint unsigned NOT NULL,
`vID` BIGINT UNSIGNED,
`vRef` BIGINT UNSIGNED,
`vTimestamp` DATETIME,
`vTime` TIME,
`vDate` DATE,
`vNumber` NUMERIC,
`vText` TEXT,
`vBoolean_T` BOOLEAN,
`vBoolean_F` BOOLEAN,
`vEnum` TEXT,
`vGeometry` TEXT,
`vJSON` TEXT,
`vBlob` BLOB,
`vUUID` VARCHAR(36),
`owned_by` bigint unsigned NOT NULL,
`created_at` datetime NOT NULL,
`updated_at` datetime DEFAULT NULL,
`deleted_at` datetime DEFAULT NULL,
`created_by` bigint unsigned NOT NULL,
`updated_by` bigint unsigned NOT NULL DEFAULT '0',
`deleted_by` bigint unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `compose_record_owner` (`owned_by`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
@@ -0,0 +1,43 @@
{
"name": "Test Connection",
"handle": "test_connection",
"type": "corteza::system:dal_connection",
"location": {
"geometry": {
"type": "",
"coordinates": null
},
"properties": {
"name": ""
}
},
"ownership": "",
"sensitivityLevel": "0",
"config": {
"defaultModelIdent": "compose_record",
"defaultAttributeIdent": "values",
"defaultPartitionFormat": "compose_record_{{namespace}}_{{module}}",
"partitionFormatValidator": "",
"connection": {
"type": "corteza::dal:connection:dsn",
"params": {
"dsn": "NO"
}
}
},
"capabilities": {
"enforced": [],
"supported": [
"corteza::dal:capability:create",
"corteza::dal:capability:update",
"corteza::dal:capability:search",
"corteza::dal:capability:lookup",
"corteza::dal:capability:paging",
"corteza::dal:capability:stats",
"corteza::dal:capability:sorting",
"corteza::dal:capability:RBAC"
],
"unsupported": [],
"enabled": []
}
}
@@ -0,0 +1,43 @@
{
"name": "Test Connection",
"handle": "test_connection",
"type": "corteza::system:primary_dal_connection",
"location": {
"geometry": {
"type": "",
"coordinates": null
},
"properties": {
"name": ""
}
},
"ownership": "",
"sensitivityLevel": "0",
"config": {
"defaultModelIdent": "compose_record",
"defaultAttributeIdent": "values",
"defaultPartitionFormat": "compose_record_{{namespace}}_{{module}}",
"partitionFormatValidator": "",
"connection": {
"type": "corteza::dal:connection:dsn",
"params": {
"dsn": "sqlite3://file::memory:?cache=shared&mode=memory"
}
}
},
"capabilities": {
"enforced": [],
"supported": [
"corteza::dal:capability:create",
"corteza::dal:capability:update",
"corteza::dal:capability:search",
"corteza::dal:capability:lookup",
"corteza::dal:capability:paging",
"corteza::dal:capability:stats",
"corteza::dal:capability:sorting",
"corteza::dal:capability:RBAC"
],
"unsupported": [],
"enabled": []
}
}
@@ -0,0 +1,44 @@
{
"name": "Test Connection",
"handle": "test_connection",
"type": "corteza::system:dal_connection",
"location": {
"geometry": {
"type": "",
"coordinates": null
},
"properties": {
"name": ""
}
},
"ownership": "",
"sensitivityLevel": "42",
"config": {
"defaultModelIdent": "compose_record",
"defaultAttributeIdent": "values",
"defaultPartitionFormat": "compose_record_{{namespace}}_{{module}}",
"partitionFormatValidator": "",
"connection": {
"type": "corteza::dal:connection:dsn",
"params": {
"dsn": "sqlite3://file::memory:?cache=shared&mode=memory"
}
}
},
"capabilities": {
"enforced": [],
"supported": [
"corteza::dal:capability:create",
"corteza::dal:capability:update",
"corteza::dal:capability:search",
"corteza::dal:capability:lookup",
"corteza::dal:capability:paging",
"corteza::dal:capability:stats",
"corteza::dal:capability:sorting",
"corteza::dal:capability:RBAC"
],
"unsupported": [],
"enabled": []
}
}
@@ -0,0 +1,35 @@
{
"name": "internal_non_partitioned",
"handle": "internal_non_partitioned",
"modelConfig": {
"connectionID": "0",
"capabilities": [],
"partitioned": false
},
"privacy": {
"sensitivityLevel": "0",
"usageDisclosure": "A"
},
"fields": [
{
"name": "name",
"kind": "String",
"privacy": {
"sensitivityLevel": "42",
"usageDisclosure": "A"
}
},
{
"name": "email",
"kind": "Email"
},
{
"name": "a_number",
"kind": "Number"
}
]
}
@@ -0,0 +1,35 @@
{
"name": "internal_non_partitioned",
"handle": "internal_non_partitioned",
"modelConfig": {
"connectionID": "0",
"capabilities": [],
"partitioned": false
},
"privacy": {
"sensitivityLevel": "42",
"usageDisclosure": "A"
},
"fields": [
{
"name": "name",
"kind": "String",
"privacy": {
"sensitivityLevel": "0",
"usageDisclosure": "A"
}
},
{
"name": "email",
"kind": "Email"
},
{
"name": "a_number",
"kind": "Number"
}
]
}
@@ -0,0 +1,35 @@
{
"name": "internal_non_partitioned",
"handle": "internal_non_partitioned",
"modelConfig": {
"connectionID": "0",
"capabilities": [],
"partitioned": false
},
"privacy": {
"sensitivityLevel": "42",
"usageDisclosure": "A"
},
"fields": [
{
"name": "name",
"kind": "String",
"privacy": {
"sensitivityLevel": "0",
"usageDisclosure": "A"
}
},
{
"name": "email",
"kind": "Email"
},
{
"name": "a_number",
"kind": "Number"
}
]
}
+43
View File
@@ -0,0 +1,43 @@
{
"name": "Test Connection",
"handle": "test_connection",
"type": "corteza::system:dal_connection",
"location": {
"geometry": {
"type": "",
"coordinates": null
},
"properties": {
"name": ""
}
},
"ownership": "",
"sensitivityLevel": "0",
"config": {
"defaultModelIdent": "compose_record",
"defaultAttributeIdent": "values",
"defaultPartitionFormat": "compose_record_{{namespace}}_{{module}}",
"partitionFormatValidator": "",
"connection": {
"type": "corteza::dal:connection:dsn",
"params": {
"dsn": "sqlite3://file::memory:?cache=shared&mode=memory"
}
}
},
"capabilities": {
"enforced": [],
"supported": [
"corteza::dal:capability:create",
"corteza::dal:capability:update",
"corteza::dal:capability:search",
"corteza::dal:capability:lookup",
"corteza::dal:capability:paging",
"corteza::dal:capability:stats",
"corteza::dal:capability:sorting",
"corteza::dal:capability:RBAC"
],
"unsupported": [],
"enabled": []
}
}
+43
View File
@@ -0,0 +1,43 @@
{
"name": "Test Connection EDITED",
"handle": "test_connection_edited",
"type": "corteza::system:dal_connection",
"location": {
"geometry": {
"type": "",
"coordinates": null
},
"properties": {
"name": ""
}
},
"ownership": "",
"sensitivityLevel": "0",
"config": {
"defaultModelIdent": "compose_record",
"defaultAttributeIdent": "values",
"defaultPartitionFormat": "compose_record_{{namespace}}_{{module}}",
"partitionFormatValidator": "",
"connection": {
"type": "corteza::dal:connection:dsn",
"params": {
"dsn": "sqlite3://file::memory:?cache=shared&mode=memory"
}
}
},
"capabilities": {
"enforced": [],
"supported": [
"corteza::dal:capability:create",
"corteza::dal:capability:update",
"corteza::dal:capability:search",
"corteza::dal:capability:lookup",
"corteza::dal:capability:paging",
"corteza::dal:capability:stats",
"corteza::dal:capability:sorting",
"corteza::dal:capability:RBAC"
],
"unsupported": [],
"enabled": []
}
}
+30
View File
@@ -0,0 +1,30 @@
{
"name": "internal_non_partitioned",
"handle": "internal_non_partitioned",
"modelConfig": {
"connectionID": "0",
"capabilities": [],
"partitioned": false
},
"privacy": {
"sensitivityLevel": "0",
"usageDisclosure": "A"
},
"fields": [
{
"name": "name",
"kind": "String"
},
{
"name": "email",
"kind": "Email"
},
{
"name": "a_number",
"kind": "Number"
}
]
}
+16
View File
@@ -0,0 +1,16 @@
{
"values": [
{
"name": "name",
"value": "Me"
},
{
"name": "email",
"value": "me@test.tld"
},
{
"name": "a_number",
"value": "42"
}
]
}
+16
View File
@@ -0,0 +1,16 @@
{
"values": [
{
"name": "name",
"value": "Me updated"
},
{
"name": "email",
"value": "me+updated@test.tld"
},
{
"name": "a_number",
"value": "43"
}
]
}
+8
View File
@@ -0,0 +1,8 @@
{
"handle": "private",
"level": 1,
"meta": {
"name": "private sensitivity",
"description": "data is private to the owner"
}
}
@@ -0,0 +1,8 @@
{
"handle": "private_edited",
"level": 1,
"meta": {
"name": "private sensitivity",
"description": "data is private to the owner"
}
}