chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,437 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os/user"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/kashav/fsql/query"
|
||||
"github.com/kashav/fsql/tokenizer"
|
||||
)
|
||||
|
||||
func TestParser_ParseSelect(t *testing.T) {
|
||||
type Expected struct {
|
||||
attributes []string
|
||||
modifiers map[string][]query.Modifier
|
||||
err error
|
||||
}
|
||||
|
||||
type Case struct {
|
||||
input string
|
||||
expected Expected
|
||||
}
|
||||
|
||||
cases := []Case{
|
||||
{
|
||||
input: "all",
|
||||
expected: Expected{
|
||||
attributes: allAttributes,
|
||||
modifiers: map[string][]query.Modifier{},
|
||||
err: nil,
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
input: "SELECT",
|
||||
expected: Expected{
|
||||
attributes: allAttributes,
|
||||
modifiers: map[string][]query.Modifier{},
|
||||
err: nil,
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
input: "FROM",
|
||||
expected: Expected{
|
||||
attributes: allAttributes,
|
||||
modifiers: map[string][]query.Modifier{},
|
||||
err: nil,
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
input: "SELECT name",
|
||||
expected: Expected{
|
||||
attributes: []string{"name"},
|
||||
modifiers: map[string][]query.Modifier{"name": {}},
|
||||
err: nil,
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
input: "SELECT format(size, kb)",
|
||||
expected: Expected{
|
||||
attributes: []string{"size"},
|
||||
modifiers: map[string][]query.Modifier{
|
||||
"size": {
|
||||
{
|
||||
Name: "FORMAT",
|
||||
Arguments: []string{"kb"},
|
||||
},
|
||||
},
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
input: "",
|
||||
expected: Expected{err: io.ErrUnexpectedEOF},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
q := query.NewQuery()
|
||||
err := (&parser{tokenizer: tokenizer.NewTokenizer(c.input)}).parseSelectClause(q)
|
||||
|
||||
if c.expected.err == nil {
|
||||
if err != nil {
|
||||
t.Fatalf("\nExpected no error\n Got %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(c.expected.attributes, q.Attributes) {
|
||||
t.Fatalf("\nExpected %v\n Got %v", c.expected.attributes, q.Attributes)
|
||||
}
|
||||
if !reflect.DeepEqual(c.expected.modifiers, q.Modifiers) {
|
||||
t.Fatalf("\nExpected %v\n Got %v", c.expected.modifiers, q.Modifiers)
|
||||
}
|
||||
} else if !reflect.DeepEqual(c.expected.err, err) {
|
||||
t.Fatalf("\nExpected %v\n Got %v", c.expected.err, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParser_ParseFrom(t *testing.T) {
|
||||
type Expected struct {
|
||||
sources map[string][]string
|
||||
aliases map[string]string
|
||||
err error
|
||||
}
|
||||
|
||||
type Case struct {
|
||||
input string
|
||||
expected Expected
|
||||
}
|
||||
|
||||
u, err := user.Current()
|
||||
if err != nil {
|
||||
// TODO: If we can't get the current user, should we fatal or just return?
|
||||
return
|
||||
}
|
||||
|
||||
cases := []Case{
|
||||
{
|
||||
input: "WHERE",
|
||||
expected: Expected{
|
||||
sources: map[string][]string{
|
||||
"include": {"."},
|
||||
"exclude": {},
|
||||
},
|
||||
aliases: map[string]string{},
|
||||
err: nil,
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
input: "FROM .",
|
||||
expected: Expected{
|
||||
sources: map[string][]string{
|
||||
"include": {"."},
|
||||
"exclude": {},
|
||||
},
|
||||
aliases: map[string]string{},
|
||||
err: nil,
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
input: "FROM ~/foo, -./.git/",
|
||||
expected: Expected{
|
||||
sources: map[string][]string{
|
||||
"include": {u.HomeDir + "/foo"},
|
||||
"exclude": {".git"},
|
||||
},
|
||||
aliases: map[string]string{},
|
||||
err: nil,
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
input: "FROM ./foo/ AS foo",
|
||||
expected: Expected{
|
||||
sources: map[string][]string{
|
||||
"include": {"foo"},
|
||||
"exclude": {},
|
||||
},
|
||||
aliases: map[string]string{"foo": "foo"},
|
||||
err: nil,
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
input: "FROM",
|
||||
expected: Expected{err: io.ErrUnexpectedEOF},
|
||||
},
|
||||
|
||||
{
|
||||
input: "FROM WHERE",
|
||||
expected: Expected{
|
||||
err: &ErrUnexpectedToken{
|
||||
Actual: tokenizer.Where,
|
||||
Expected: tokenizer.Identifier,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
q := query.NewQuery()
|
||||
err := (&parser{tokenizer: tokenizer.NewTokenizer(c.input)}).parseFromClause(q)
|
||||
|
||||
if c.expected.err == nil {
|
||||
if err != nil {
|
||||
t.Fatalf("\nExpected no error\n Got %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(c.expected.sources, q.Sources) {
|
||||
t.Fatalf("\nExpected %v\n Got %v", c.expected.sources, q.Sources)
|
||||
}
|
||||
if !reflect.DeepEqual(c.expected.aliases, q.SourceAliases) {
|
||||
t.Fatalf("\nExpected %v\n Got %v", c.expected.aliases, q.SourceAliases)
|
||||
}
|
||||
} else if !reflect.DeepEqual(c.expected.err, err) {
|
||||
t.Fatalf("\nExpected %v\n Got %v", c.expected.err, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParser_ParseWhere(t *testing.T) {
|
||||
type Expected struct {
|
||||
tree *query.ConditionNode
|
||||
err error
|
||||
}
|
||||
|
||||
type Case struct {
|
||||
input string
|
||||
expected Expected
|
||||
}
|
||||
|
||||
cases := []Case{
|
||||
{
|
||||
input: "WHERE name LIKE foo",
|
||||
expected: Expected{
|
||||
tree: &query.ConditionNode{
|
||||
Condition: &query.Condition{
|
||||
Attribute: "name",
|
||||
Operator: tokenizer.Like,
|
||||
Value: "foo",
|
||||
},
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
},
|
||||
|
||||
// Our tree is fully-zeroed in this case, so it's easier just to give it
|
||||
// an empty Expected struct.
|
||||
{input: "", expected: Expected{}},
|
||||
|
||||
{input: "WHERE", expected: Expected{err: io.ErrUnexpectedEOF}},
|
||||
|
||||
{
|
||||
input: "name LIKE foo",
|
||||
expected: Expected{
|
||||
err: &ErrUnexpectedToken{
|
||||
Expected: tokenizer.Where,
|
||||
Actual: tokenizer.Identifier,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
q := query.NewQuery()
|
||||
err := (&parser{tokenizer: tokenizer.NewTokenizer(c.input)}).parseWhereClause(q)
|
||||
|
||||
if c.expected.err == nil {
|
||||
if err != nil {
|
||||
t.Fatalf("\nExpected no error\n Got %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(c.expected.tree, q.ConditionTree) {
|
||||
t.Fatalf("\nExpected %v\n Got %v", c.expected.tree, q.ConditionTree)
|
||||
}
|
||||
} else if !reflect.DeepEqual(c.expected.err, err) {
|
||||
t.Fatalf("\nExpected %v\n Got %v", c.expected.err, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParser_Expect(t *testing.T) {
|
||||
type Case struct {
|
||||
param tokenizer.TokenType
|
||||
expected *tokenizer.Token
|
||||
}
|
||||
|
||||
input := "SELECT all FROM . WHERE name = foo OR size <> 100"
|
||||
p := &parser{tokenizer: tokenizer.NewTokenizer(input)}
|
||||
|
||||
cases := []Case{
|
||||
{
|
||||
param: tokenizer.Select,
|
||||
expected: &tokenizer.Token{Type: tokenizer.Select, Raw: "SELECT"},
|
||||
},
|
||||
{
|
||||
param: tokenizer.From,
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
param: tokenizer.Identifier,
|
||||
expected: &tokenizer.Token{Type: tokenizer.Identifier, Raw: "all"},
|
||||
},
|
||||
{
|
||||
param: tokenizer.Identifier,
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
param: tokenizer.From,
|
||||
expected: &tokenizer.Token{Type: tokenizer.From, Raw: "FROM"},
|
||||
},
|
||||
{
|
||||
param: tokenizer.Identifier,
|
||||
expected: &tokenizer.Token{Type: tokenizer.Identifier, Raw: "."},
|
||||
},
|
||||
{
|
||||
param: tokenizer.Identifier,
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
param: tokenizer.Where,
|
||||
expected: &tokenizer.Token{Type: tokenizer.Where, Raw: "WHERE"},
|
||||
},
|
||||
{
|
||||
param: tokenizer.Identifier,
|
||||
expected: &tokenizer.Token{Type: tokenizer.Identifier, Raw: "name"},
|
||||
},
|
||||
{
|
||||
param: tokenizer.Equals,
|
||||
expected: &tokenizer.Token{Type: tokenizer.Equals, Raw: "="},
|
||||
},
|
||||
{
|
||||
param: tokenizer.Identifier,
|
||||
expected: &tokenizer.Token{Type: tokenizer.Identifier, Raw: "foo"},
|
||||
},
|
||||
{
|
||||
param: tokenizer.Or,
|
||||
expected: &tokenizer.Token{Type: tokenizer.Or, Raw: "OR"},
|
||||
},
|
||||
{
|
||||
param: tokenizer.Identifier,
|
||||
expected: &tokenizer.Token{Type: tokenizer.Identifier, Raw: "size"},
|
||||
},
|
||||
{
|
||||
param: tokenizer.Identifier,
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
param: tokenizer.NotEquals,
|
||||
expected: &tokenizer.Token{Type: tokenizer.NotEquals, Raw: "<>"},
|
||||
},
|
||||
{
|
||||
param: tokenizer.Identifier,
|
||||
expected: &tokenizer.Token{Type: tokenizer.Identifier, Raw: "100"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
actual := p.expect(c.param)
|
||||
if !reflect.DeepEqual(c.expected, actual) {
|
||||
t.Fatalf("\nExpected %v\n Got %v", c.expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParser_SelectAllVariations(t *testing.T) {
|
||||
expected := &query.Query{
|
||||
Attributes: allAttributes,
|
||||
Sources: map[string][]string{
|
||||
"include": {"."},
|
||||
"exclude": {},
|
||||
},
|
||||
ConditionTree: &query.ConditionNode{
|
||||
Condition: &query.Condition{
|
||||
Attribute: "name",
|
||||
Operator: tokenizer.Like,
|
||||
Value: "foo",
|
||||
},
|
||||
},
|
||||
SourceAliases: map[string]string{},
|
||||
Modifiers: map[string][]query.Modifier{},
|
||||
}
|
||||
|
||||
cases := []string{
|
||||
"FROM . WHERE name LIKE foo",
|
||||
"all FROM . WHERE name LIKE foo",
|
||||
"SELECT FROM . WHERE name LIKE foo",
|
||||
"SELECT all FROM . WHERE name LIKE foo",
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
actual, err := Run(c)
|
||||
if err != nil {
|
||||
t.Fatalf("\nExpected no error\n Got %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(expected, actual) {
|
||||
t.Fatalf("\nExpected %v\n Got %v", expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParser_Run(t *testing.T) {
|
||||
type Expected struct {
|
||||
q *query.Query
|
||||
err error
|
||||
}
|
||||
|
||||
type Case struct {
|
||||
input string
|
||||
expected Expected
|
||||
}
|
||||
|
||||
// TODO: Add more cases.
|
||||
cases := []Case{
|
||||
{
|
||||
input: "SELECT all FROM . WHERE name LIKE foo",
|
||||
expected: Expected{
|
||||
q: &query.Query{
|
||||
Attributes: allAttributes,
|
||||
Sources: map[string][]string{
|
||||
"include": {"."},
|
||||
"exclude": {},
|
||||
},
|
||||
ConditionTree: &query.ConditionNode{
|
||||
Condition: &query.Condition{
|
||||
Attribute: "name",
|
||||
Operator: tokenizer.Like,
|
||||
Value: "foo",
|
||||
},
|
||||
},
|
||||
SourceAliases: map[string]string{},
|
||||
Modifiers: map[string][]query.Modifier{},
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
actual, err := Run(c.input)
|
||||
if c.expected.err == nil {
|
||||
if err != nil {
|
||||
t.Fatalf("\nExpected no error\n Got %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(c.expected.q, actual) {
|
||||
t.Fatalf("\nExpected %v\n Got %v", c.expected.q, actual)
|
||||
}
|
||||
} else if !reflect.DeepEqual(c.expected.err, err) {
|
||||
t.Fatalf("\nExpected %v\n Got %v", c.expected.err, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user