1
0
mirror of https://github.com/MontFerret/ferret.git synced 2024-12-16 11:37:36 +02:00
ferret/pkg/compiler/compiler_for_test.go
2018-10-24 19:40:57 -04:00

610 lines
11 KiB
Go

package compiler_test
import (
"context"
"encoding/json"
"github.com/MontFerret/ferret/pkg/compiler"
"github.com/MontFerret/ferret/pkg/runtime"
. "github.com/smartystreets/goconvey/convey"
"sort"
"testing"
)
func TestFor(t *testing.T) {
Convey("Should compile FOR i IN [] RETURN i", t, func() {
c := compiler.New()
p, err := c.Compile(`
FOR i IN []
RETURN i
`)
So(err, ShouldBeNil)
So(p, ShouldHaveSameTypeAs, &runtime.Program{})
out, err := p.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, "[]")
})
Convey("Should compile FOR i IN [1, 2, 3] RETURN i", t, func() {
c := compiler.New()
p, err := c.Compile(`
FOR i IN [1, 2, 3]
RETURN i
`)
So(err, ShouldBeNil)
So(p, ShouldHaveSameTypeAs, &runtime.Program{})
out, err := p.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, "[1,2,3]")
})
Convey("Should compile FOR i, k IN [1, 2, 3] RETURN k", t, func() {
c := compiler.New()
p, err := c.Compile(`
FOR i, k IN [1, 2, 3]
RETURN k
`)
So(err, ShouldBeNil)
So(p, ShouldHaveSameTypeAs, &runtime.Program{})
out, err := p.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, "[0,1,2]")
})
Convey("Should compile FOR i IN ['foo', 'bar', 'qaz'] RETURN i", t, func() {
c := compiler.New()
p, err := c.Compile(`
FOR i IN ['foo', 'bar', 'qaz']
RETURN i
`)
So(err, ShouldBeNil)
So(p, ShouldHaveSameTypeAs, &runtime.Program{})
out, err := p.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, "[\"foo\",\"bar\",\"qaz\"]")
})
Convey("Should compile FOR i IN {a: 'bar', b: 'foo', c: 'qaz'} RETURN i.name", t, func() {
c := compiler.New()
p, err := c.Compile(`
FOR i IN {a: 'bar', b: 'foo', c: 'qaz'}
RETURN i
`)
So(err, ShouldBeNil)
So(p, ShouldHaveSameTypeAs, &runtime.Program{})
out, err := p.Run(context.Background())
So(err, ShouldBeNil)
arr := make([]string, 0, 3)
err = json.Unmarshal(out, &arr)
So(err, ShouldBeNil)
sort.Strings(arr)
out, err = json.Marshal(arr)
So(err, ShouldBeNil)
So(string(out), ShouldEqual, "[\"bar\",\"foo\",\"qaz\"]")
})
Convey("Should compile FOR i, k IN {a: 'foo', b: 'bar', c: 'qaz'} RETURN k", t, func() {
c := compiler.New()
p, err := c.Compile(`
FOR i, k IN {a: 'foo', b: 'bar', c: 'qaz'}
RETURN k
`)
So(err, ShouldBeNil)
So(p, ShouldHaveSameTypeAs, &runtime.Program{})
out, err := p.Run(context.Background())
So(err, ShouldBeNil)
arr := make([]string, 0, 3)
err = json.Unmarshal(out, &arr)
So(err, ShouldBeNil)
sort.Strings(arr)
out, err = json.Marshal(arr)
So(err, ShouldBeNil)
So(string(out), ShouldEqual, "[\"a\",\"b\",\"c\"]")
})
Convey("Should compile FOR i IN [{name: 'foo'}, {name: 'bar'}, {name: 'qaz'}] RETURN i.name", t, func() {
c := compiler.New()
p, err := c.Compile(`
FOR i IN [{name: 'foo'}, {name: 'bar'}, {name: 'qaz'}]
RETURN i.name
`)
So(err, ShouldBeNil)
So(p, ShouldHaveSameTypeAs, &runtime.Program{})
out, err := p.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, "[\"foo\",\"bar\",\"qaz\"]")
})
Convey("Should compile nested FOR operators", t, func() {
c := compiler.New()
p, err := c.Compile(`
FOR prop IN ["a"]
FOR val IN [1, 2, 3]
RETURN {[prop]: val}
`)
So(err, ShouldBeNil)
out, err := p.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, "[{\"a\":1},{\"a\":2},{\"a\":3}]")
})
Convey("Should compile deeply nested FOR operators", t, func() {
c := compiler.New()
p, err := c.Compile(`
FOR prop IN ["a"]
FOR val IN [1, 2, 3]
FOR val2 IN [1, 2, 3]
RETURN { [prop]: [val, val2] }
`)
So(err, ShouldBeNil)
out, err := p.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `[{"a":[1,1]},{"a":[1,2]},{"a":[1,3]},{"a":[2,1]},{"a":[2,2]},{"a":[2,3]},{"a":[3,1]},{"a":[3,2]},{"a":[3,3]}]`)
})
Convey("Should compile query with a sub query", t, func() {
c := compiler.New()
p, err := c.Compile(`
FOR val IN [1, 2, 3]
RETURN (
FOR prop IN ["a", "b", "c"]
RETURN { [prop]: val }
)
`)
So(err, ShouldBeNil)
out, err := p.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `[[{"a":1},{"b":1},{"c":1}],[{"a":2},{"b":2},{"c":2}],[{"a":3},{"b":3},{"c":3}]]`)
})
Convey("Should compile query with variable in a body", t, func() {
c := compiler.New()
p, err := c.Compile(`
FOR val IN [1, 2, 3]
LET sub = (
FOR prop IN ["a", "b", "c"]
RETURN { [prop]: val }
)
RETURN sub
`)
So(err, ShouldBeNil)
out, err := p.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `[[{"a":1},{"b":1},{"c":1}],[{"a":2},{"b":2},{"c":2}],[{"a":3},{"b":3},{"c":3}]]`)
})
Convey("Should compile query with RETURN DISTINCT", t, func() {
c := compiler.New()
p, err := c.Compile(`
FOR i IN [ 1, 2, 3, 4, 1, 3 ]
RETURN DISTINCT i
`)
So(err, ShouldBeNil)
out, err := p.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `[1,2,3,4]`)
})
Convey("Should compile query with LIMIT 2", t, func() {
c := compiler.New()
p, err := c.Compile(`
FOR i IN [ 1, 2, 3, 4, 1, 3 ]
LIMIT 2
RETURN i
`)
So(err, ShouldBeNil)
out, err := p.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `[1,2]`)
})
Convey("Should compile query with LIMIT 2, 2", t, func() {
c := compiler.New()
// 4 is offset
// 2 is count
p, err := c.Compile(`
FOR i IN [ 1,2,3,4,5,6,7,8 ]
LIMIT 4, 2
RETURN i
`)
So(err, ShouldBeNil)
out, err := p.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `[5,6]`)
})
Convey("Should compile query with SORT statement", t, func() {
c := compiler.New()
p, err := c.Compile(`
LET users = [
{
active: true,
age: 31,
gender: "m"
},
{
active: true,
age: 29,
gender: "f"
},
{
active: true,
age: 36,
gender: "m"
}
]
FOR u IN users
SORT u.age
RETURN u
`)
So(err, ShouldBeNil)
out, err := p.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `[{"active":true,"age":29,"gender":"f"},{"active":true,"age":31,"gender":"m"},{"active":true,"age":36,"gender":"m"}]`)
})
Convey("Should compile query with SORT DESC statement", t, func() {
c := compiler.New()
p, err := c.Compile(`
LET users = [
{
active: true,
age: 31,
gender: "m"
},
{
active: true,
age: 29,
gender: "f"
},
{
active: true,
age: 36,
gender: "m"
}
]
FOR u IN users
SORT u.age DESC
RETURN u
`)
So(err, ShouldBeNil)
out, err := p.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `[{"active":true,"age":36,"gender":"m"},{"active":true,"age":31,"gender":"m"},{"active":true,"age":29,"gender":"f"}]`)
})
Convey("Should compile query with SORT statement with multiple expressions", t, func() {
c := compiler.New()
p, err := c.Compile(`
LET users = [
{
active: true,
age: 31,
gender: "m"
},
{
active: true,
age: 29,
gender: "f"
},
{
active: true,
age: 31,
gender: "f"
},
{
active: true,
age: 36,
gender: "m"
}
]
FOR u IN users
SORT u.age, u.gender
RETURN u
`)
So(err, ShouldBeNil)
out, err := p.Run(context.Background())
So(err, ShouldBeNil)
So(string(out), ShouldEqual, `[{"active":true,"age":29,"gender":"f"},{"active":true,"age":31,"gender":"f"},{"active":true,"age":31,"gender":"m"},{"active":true,"age":36,"gender":"m"}]`)
})
}
func BenchmarkForEmpty(b *testing.B) {
p := compiler.New().MustCompile(`
FOR i IN []
RETURN i
`)
for n := 0; n < b.N; n++ {
p.Run(context.Background())
}
}
func BenchmarkForArray(b *testing.B) {
p := compiler.New().MustCompile(`
FOR i IN [1,2,3]
RETURN i
`)
for n := 0; n < b.N; n++ {
p.Run(context.Background())
}
}
func BenchmarkForObject(b *testing.B) {
p := compiler.New().MustCompile(`
FOR i IN {a: 'bar', b: 'foo', c: 'qaz'}
RETURN i
`)
for n := 0; n < b.N; n++ {
p.Run(context.Background())
}
}
func BenchmarkForNested(b *testing.B) {
p := compiler.New().MustCompile(`
FOR prop IN ["a"]
FOR val IN [1, 2, 3]
RETURN {[prop]: val}
`)
for n := 0; n < b.N; n++ {
p.Run(context.Background())
}
}
func BenchmarkForNested2(b *testing.B) {
p := compiler.New().MustCompile(`
FOR prop IN ["a"]
FOR val IN [1, 2, 3]
FOR val2 IN ["b"]
RETURN { [prop]: [val, val2] }
`)
for n := 0; n < b.N; n++ {
p.Run(context.Background())
}
}
func BenchmarkForSub(b *testing.B) {
p := compiler.New().MustCompile(`
FOR val IN [1, 2, 3]
RETURN (
FOR prop IN ["a", "b", "c"]
RETURN { [prop]: val }
)
`)
for n := 0; n < b.N; n++ {
p.Run(context.Background())
}
}
func BenchmarkForSub2(b *testing.B) {
p := compiler.New().MustCompile(`
FOR val IN [1, 2, 3]
LET sub = (
FOR prop IN ["a", "b", "c"]
RETURN { [prop]: val }
)
RETURN sub
`)
for n := 0; n < b.N; n++ {
p.Run(context.Background())
}
}
func BenchmarkForDistinct(b *testing.B) {
p := compiler.New().MustCompile(`
FOR i IN [ 1, 2, 3, 4, 1, 3 ]
RETURN DISTINCT i
`)
for n := 0; n < b.N; n++ {
p.Run(context.Background())
}
}
func BenchmarkForLimit(b *testing.B) {
p := compiler.New().MustCompile(`
FOR i IN [ 1,2,3,4,5,6,7,8 ]
LIMIT 2
RETURN i
`)
for n := 0; n < b.N; n++ {
p.Run(context.Background())
}
}
func BenchmarkForLimitOffset(b *testing.B) {
p := compiler.New().MustCompile(`
FOR i IN [ 1,2,3,4,5,6,7,8 ]
LIMIT 4, 2
RETURN i
`)
for n := 0; n < b.N; n++ {
p.Run(context.Background())
}
}
func BenchmarkForSort(b *testing.B) {
p := compiler.New().MustCompile(`
LET users = [
{
active: true,
age: 31,
gender: "m"
},
{
active: true,
age: 29,
gender: "f"
},
{
active: true,
age: 36,
gender: "m"
}
]
FOR u IN users
SORT u.age
RETURN u
`)
for n := 0; n < b.N; n++ {
p.Run(context.Background())
}
}
func BenchmarkForSort2(b *testing.B) {
p := compiler.New().MustCompile(`
LET users = [
{
active: true,
age: 31,
gender: "m"
},
{
active: true,
age: 29,
gender: "f"
},
{
active: true,
age: 36,
gender: "m"
}
]
FOR u IN users
SORT u.age, u.gender
RETURN u
`)
for n := 0; n < b.N; n++ {
p.Run(context.Background())
}
}
func BenchmarkForSortDesc(b *testing.B) {
p := compiler.New().MustCompile(`
LET users = [
{
active: true,
age: 31,
gender: "m"
},
{
active: true,
age: 29,
gender: "f"
},
{
active: true,
age: 36,
gender: "m"
}
]
FOR u IN users
SORT u.age DESC
RETURN u
`)
for n := 0; n < b.N; n++ {
p.Run(context.Background())
}
}