mirror of
https://github.com/chaisql/chai.git
synced 2025-10-05 07:36:56 +08:00
330 lines
12 KiB
Go
330 lines
12 KiB
Go
package database_test
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/genjidb/genji/document"
|
|
"github.com/genjidb/genji/internal/database"
|
|
"github.com/genjidb/genji/internal/expr"
|
|
"github.com/genjidb/genji/internal/testutil"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestFieldConstraintsInfer(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
got, want database.FieldConstraints
|
|
fails bool
|
|
}{
|
|
{
|
|
"No change",
|
|
[]*database.FieldConstraint{{Path: document.NewPath("a"), Type: document.IntegerValue}},
|
|
[]*database.FieldConstraint{{Path: document.NewPath("a"), Type: document.IntegerValue}},
|
|
false,
|
|
},
|
|
{
|
|
"Array",
|
|
[]*database.FieldConstraint{{Path: document.NewPath("a", "0"), Type: document.IntegerValue}},
|
|
[]*database.FieldConstraint{
|
|
{Path: document.NewPath("a"), Type: document.ArrayValue, IsInferred: true, InferredBy: []document.Path{document.NewPath("a", "0")}},
|
|
{Path: document.NewPath("a", "0"), Type: document.IntegerValue},
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"Document",
|
|
[]*database.FieldConstraint{{Path: document.NewPath("a", "b"), Type: document.IntegerValue}},
|
|
[]*database.FieldConstraint{
|
|
{Path: document.NewPath("a"), Type: document.DocumentValue, IsInferred: true, InferredBy: []document.Path{document.NewPath("a", "b")}},
|
|
{Path: document.NewPath("a", "b"), Type: document.IntegerValue},
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"Primary key",
|
|
[]*database.FieldConstraint{
|
|
{Path: document.NewPath("a"), Type: document.ArrayValue, IsPrimaryKey: true},
|
|
{Path: document.NewPath("a", "0"), Type: document.IntegerValue},
|
|
},
|
|
[]*database.FieldConstraint{
|
|
{Path: document.NewPath("a"), Type: document.ArrayValue, IsPrimaryKey: true},
|
|
{Path: document.NewPath("a", "0"), Type: document.IntegerValue},
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"Complex path",
|
|
[]*database.FieldConstraint{{Path: document.NewPath("a", "b", "3", "1", "c"), Type: document.IntegerValue}},
|
|
[]*database.FieldConstraint{
|
|
{Path: document.NewPath("a"), Type: document.DocumentValue, IsInferred: true, InferredBy: []document.Path{document.NewPath("a", "b", "3", "1", "c")}},
|
|
{Path: document.NewPath("a", "b"), Type: document.ArrayValue, IsInferred: true, InferredBy: []document.Path{document.NewPath("a", "b", "3", "1", "c")}},
|
|
{Path: document.NewPath("a", "b", "3"), Type: document.ArrayValue, IsInferred: true, InferredBy: []document.Path{document.NewPath("a", "b", "3", "1", "c")}},
|
|
{Path: document.NewPath("a", "b", "3", "1"), Type: document.DocumentValue, IsInferred: true, InferredBy: []document.Path{document.NewPath("a", "b", "3", "1", "c")}},
|
|
{Path: document.NewPath("a", "b", "3", "1", "c"), Type: document.IntegerValue},
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"Overlaping constraints",
|
|
[]*database.FieldConstraint{
|
|
{Path: document.NewPath("a", "b"), Type: document.IntegerValue},
|
|
{Path: document.NewPath("a", "c"), Type: document.IntegerValue},
|
|
},
|
|
[]*database.FieldConstraint{
|
|
{Path: document.NewPath("a"), Type: document.DocumentValue, IsInferred: true, InferredBy: []document.Path{document.NewPath("a", "b"), document.NewPath("a", "c")}},
|
|
{Path: document.NewPath("a", "b"), Type: document.IntegerValue},
|
|
{Path: document.NewPath("a", "c"), Type: document.IntegerValue},
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"Same path inferred and non inferred: inferred first",
|
|
[]*database.FieldConstraint{
|
|
{Path: document.NewPath("a", "b"), Type: document.IntegerValue},
|
|
{Path: document.NewPath("a"), Type: document.DocumentValue},
|
|
},
|
|
[]*database.FieldConstraint{
|
|
{Path: document.NewPath("a"), Type: document.DocumentValue},
|
|
{Path: document.NewPath("a", "b"), Type: document.IntegerValue},
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"Same path inferred and non inferred: inferred last",
|
|
[]*database.FieldConstraint{
|
|
{Path: document.NewPath("a"), Type: document.DocumentValue},
|
|
{Path: document.NewPath("a", "b"), Type: document.IntegerValue},
|
|
},
|
|
[]*database.FieldConstraint{
|
|
{Path: document.NewPath("a"), Type: document.DocumentValue},
|
|
{Path: document.NewPath("a", "b"), Type: document.IntegerValue},
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"Complex case",
|
|
[]*database.FieldConstraint{
|
|
{Path: document.NewPath("a", "b", "3", "1", "c"), Type: document.DocumentValue},
|
|
{Path: document.NewPath("a", "b", "3", "1", "c", "d"), Type: document.IntegerValue},
|
|
{Path: document.NewPath("a", "b", "2"), Type: document.IntegerValue},
|
|
},
|
|
[]*database.FieldConstraint{
|
|
{Path: document.NewPath("a"), Type: document.DocumentValue, IsInferred: true,
|
|
InferredBy: []document.Path{document.NewPath("a", "b", "3", "1", "c"), document.NewPath("a", "b", "3", "1", "c", "d"), document.NewPath("a", "b", "2")}},
|
|
{Path: document.NewPath("a", "b"), Type: document.ArrayValue, IsInferred: true,
|
|
InferredBy: []document.Path{document.NewPath("a", "b", "3", "1", "c"), document.NewPath("a", "b", "3", "1", "c", "d"), document.NewPath("a", "b", "2")}},
|
|
{Path: document.NewPath("a", "b", "3"), Type: document.ArrayValue, IsInferred: true,
|
|
InferredBy: []document.Path{document.NewPath("a", "b", "3", "1", "c"), document.NewPath("a", "b", "3", "1", "c", "d")}},
|
|
{Path: document.NewPath("a", "b", "3", "1"), Type: document.DocumentValue, IsInferred: true,
|
|
InferredBy: []document.Path{document.NewPath("a", "b", "3", "1", "c"), document.NewPath("a", "b", "3", "1", "c", "d")}},
|
|
{Path: document.NewPath("a", "b", "3", "1", "c"), Type: document.DocumentValue},
|
|
{Path: document.NewPath("a", "b", "3", "1", "c", "d"), Type: document.IntegerValue},
|
|
{Path: document.NewPath("a", "b", "2"), Type: document.IntegerValue},
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"Same path, different constraint",
|
|
[]*database.FieldConstraint{
|
|
{Path: document.NewPath("a", "b"), Type: document.IntegerValue},
|
|
{Path: document.NewPath("a", "b"), Type: document.DoubleValue},
|
|
},
|
|
nil,
|
|
true,
|
|
},
|
|
{
|
|
"Inferred constraint first, conflict with non inferred",
|
|
[]*database.FieldConstraint{
|
|
{Path: document.NewPath("a", "b"), Type: document.IntegerValue},
|
|
{Path: document.NewPath("a"), Type: document.IntegerValue},
|
|
},
|
|
nil,
|
|
true,
|
|
},
|
|
{
|
|
"Non inferred constraint first, conflict with inferred",
|
|
[]*database.FieldConstraint{
|
|
{Path: document.NewPath("a"), Type: document.IntegerValue},
|
|
{Path: document.NewPath("a", "b"), Type: document.IntegerValue},
|
|
},
|
|
nil,
|
|
true,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
got, err := test.got.Infer()
|
|
if test.fails {
|
|
require.Error(t, err)
|
|
} else {
|
|
require.NoError(t, err)
|
|
require.Equal(t, test.want, got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFieldConstraintsAdd(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
got database.FieldConstraints
|
|
add database.FieldConstraint
|
|
want database.FieldConstraints
|
|
fails bool
|
|
}{
|
|
{
|
|
"Same path",
|
|
[]*database.FieldConstraint{{Path: document.NewPath("a"), Type: document.IntegerValue}},
|
|
database.FieldConstraint{Path: document.NewPath("a"), Type: document.IntegerValue},
|
|
nil,
|
|
true,
|
|
},
|
|
{
|
|
"Duplicate primary key",
|
|
[]*database.FieldConstraint{{Path: document.NewPath("a"), IsPrimaryKey: true, Type: document.IntegerValue}},
|
|
database.FieldConstraint{Path: document.NewPath("b"), IsPrimaryKey: true, Type: document.IntegerValue},
|
|
nil,
|
|
true,
|
|
},
|
|
{
|
|
"Different path",
|
|
[]*database.FieldConstraint{{Path: document.NewPath("a"), Type: document.IntegerValue}},
|
|
database.FieldConstraint{Path: document.NewPath("b"), Type: document.IntegerValue},
|
|
[]*database.FieldConstraint{
|
|
{Path: document.NewPath("a"), Type: document.IntegerValue},
|
|
{Path: document.NewPath("b"), Type: document.IntegerValue},
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"Default value conversion, typed constraint",
|
|
[]*database.FieldConstraint{{Path: document.NewPath("a"), Type: document.IntegerValue}},
|
|
database.FieldConstraint{Path: document.NewPath("b"), Type: document.IntegerValue, DefaultValue: expr.Constraint(testutil.DoubleValue(5))},
|
|
[]*database.FieldConstraint{
|
|
{Path: document.NewPath("a"), Type: document.IntegerValue},
|
|
{Path: document.NewPath("b"), Type: document.IntegerValue, DefaultValue: expr.Constraint(testutil.DoubleValue(5))},
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"Default value conversion, typed constraint, NEXT VALUE FOR",
|
|
[]*database.FieldConstraint{{Path: document.NewPath("a"), Type: document.IntegerValue}},
|
|
database.FieldConstraint{Path: document.NewPath("b"), Type: document.IntegerValue, DefaultValue: expr.Constraint(expr.NextValueFor{SeqName: "seq"})},
|
|
[]*database.FieldConstraint{
|
|
{Path: document.NewPath("a"), Type: document.IntegerValue},
|
|
{Path: document.NewPath("b"), Type: document.IntegerValue, DefaultValue: expr.Constraint(expr.NextValueFor{SeqName: "seq"})},
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"Default value conversion, typed constraint, NEXT VALUE FOR with blob",
|
|
[]*database.FieldConstraint{{Path: document.NewPath("a"), Type: document.IntegerValue}},
|
|
database.FieldConstraint{Path: document.NewPath("b"), Type: document.BlobValue, DefaultValue: expr.Constraint(expr.NextValueFor{SeqName: "seq"})},
|
|
nil,
|
|
true,
|
|
},
|
|
{
|
|
"Default value conversion, typed constraint, incompatible value",
|
|
[]*database.FieldConstraint{{Path: document.NewPath("a"), Type: document.IntegerValue}},
|
|
database.FieldConstraint{Path: document.NewPath("b"), Type: document.DoubleValue, DefaultValue: expr.Constraint(testutil.BoolValue(true))},
|
|
nil,
|
|
true,
|
|
},
|
|
{
|
|
"Default value conversion, untyped constraint",
|
|
[]*database.FieldConstraint{{Path: document.NewPath("a"), Type: document.IntegerValue}},
|
|
database.FieldConstraint{Path: document.NewPath("b"), DefaultValue: expr.Constraint(testutil.IntegerValue(5))},
|
|
[]*database.FieldConstraint{
|
|
{Path: document.NewPath("a"), Type: document.IntegerValue},
|
|
{Path: document.NewPath("b"), DefaultValue: expr.Constraint(testutil.IntegerValue(5))},
|
|
},
|
|
false,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
err := test.got.Add(&test.add)
|
|
if test.fails {
|
|
require.Error(t, err)
|
|
} else {
|
|
require.NoError(t, err)
|
|
require.Equal(t, test.want, test.got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFieldConstraintsConvert(t *testing.T) {
|
|
tests := []struct {
|
|
constraints database.FieldConstraints
|
|
path document.Path
|
|
in, want document.Value
|
|
fails bool
|
|
}{
|
|
{
|
|
nil,
|
|
document.NewPath("a"),
|
|
document.NewIntegerValue(10),
|
|
document.NewDoubleValue(10),
|
|
false,
|
|
},
|
|
{
|
|
database.FieldConstraints{{Path: document.NewPath("a"), Type: document.IntegerValue}},
|
|
document.NewPath("a"),
|
|
document.NewIntegerValue(10),
|
|
document.NewIntegerValue(10),
|
|
false,
|
|
},
|
|
{
|
|
database.FieldConstraints{{Path: document.NewPath("a"), Type: document.IntegerValue}},
|
|
document.NewPath("a"),
|
|
document.NewDoubleValue(10.5),
|
|
document.NewIntegerValue(10),
|
|
false,
|
|
},
|
|
{
|
|
database.FieldConstraints{{Path: document.NewPath("a", "0"), Type: document.IntegerValue}},
|
|
document.NewPath("a"),
|
|
document.NewArrayValue(testutil.MakeArray(t, `[10.5, 10.5]`)),
|
|
document.NewArrayValue(testutil.MakeArray(t, `[10, 10.5]`)),
|
|
false,
|
|
},
|
|
{
|
|
database.FieldConstraints{{Path: document.NewPath("a", "b"), Type: document.IntegerValue}},
|
|
document.NewPath("a"),
|
|
document.NewDocumentValue(testutil.MakeDocument(t, `{"b": 10.5, "c": 10.5}`)),
|
|
document.NewDocumentValue(testutil.MakeDocument(t, `{"b": 10, "c": 10.5}`)),
|
|
false,
|
|
},
|
|
{
|
|
database.FieldConstraints{{Path: document.NewPath("a"), Type: document.IntegerValue}},
|
|
document.NewPath("a"),
|
|
document.NewTextValue("foo"),
|
|
document.NewTextValue("foo"),
|
|
true,
|
|
},
|
|
{
|
|
database.FieldConstraints{{Path: document.NewPath("a"), DefaultValue: expr.Constraint(testutil.IntegerValue(10))}},
|
|
document.NewPath("a"),
|
|
document.NewTextValue("foo"),
|
|
document.NewTextValue("foo"),
|
|
false,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(fmt.Sprintf("%s / %v to %v", test.path, test.in, test.want), func(t *testing.T) {
|
|
got, err := test.constraints.ConvertValueAtPath(test.path, test.in, database.CastConversion)
|
|
if test.fails {
|
|
require.Error(t, err)
|
|
} else {
|
|
require.NoError(t, err)
|
|
require.Equal(t, test.want, got)
|
|
}
|
|
})
|
|
}
|
|
}
|