Skip to content
Snippets Groups Projects
Commit 1d0f3385 authored by thijsheijden's avatar thijsheijden
Browse files

Added convertquery unit tests to cover 91.1% of the conversion code

parent 6a68d13a
No related branches found
No related tags found
No related merge requests found
...@@ -2,6 +2,7 @@ package convertquery ...@@ -2,6 +2,7 @@ package convertquery
import ( import (
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
) )
...@@ -19,6 +20,34 @@ func (s *Service) ConvertQuery(jsonMsg *[]byte) (*string, error) { ...@@ -19,6 +20,34 @@ func (s *Service) ConvertQuery(jsonMsg *[]byte) (*string, error) {
return nil, err return nil, err
} }
// Check to make sure all indexes exist
// How many entities are there
numEntities := len(jsonStruct.Entities) - 1
// How many relations there are
numRelations := len(jsonStruct.Relations) - 1
// Make sure no entity should be returned that is outside the range of that list
for _, e := range jsonStruct.Return.Entities {
// If this entity references an entity that is outside the range
if e > numEntities || e < 0 {
return nil, errors.New("non-existing entity referenced in return")
}
}
// Make sure that no relation mentions a non-existing entity
for _, r := range jsonStruct.Relations {
if r.EntityFrom > numEntities || r.EntityTo > numEntities {
return nil, errors.New("non-exisiting entity referenced in relation")
}
}
// Make sure no non-existing relation is tried to be returned
for _, r := range jsonStruct.Return.Relations {
if r > numRelations || r < 0 {
return nil, errors.New("non-existing relation referenced in return")
}
}
result := createQuery(jsonStruct) result := createQuery(jsonStruct)
return result, nil return result, nil
} }
...@@ -66,13 +95,21 @@ func createQuery(jsonQuery *parsedJSON) *string { ...@@ -66,13 +95,21 @@ func createQuery(jsonQuery *parsedJSON) *string {
// if there is a from-node // if there is a from-node
// create the let for this node // create the let for this node
fromName := fmt.Sprintf("n%v", relation.EntityFrom) fromName := fmt.Sprintf("n%v", relation.EntityFrom)
ret += *createNodeLet(&jsonQuery.Entities[relation.EntityFrom], &fromName)
// Check if this entity index exists
if entity := &jsonQuery.Entities[relation.EntityFrom]; entity != nil {
ret += *createNodeLet(entity, &fromName)
}
ret += *createRelationLetWithFromEntity(&relation, relationName, &jsonQuery.Entities, jsonQuery.Limit) ret += *createRelationLetWithFromEntity(&relation, relationName, &jsonQuery.Entities, jsonQuery.Limit)
} else if relation.EntityTo != -1 { } else if relation.EntityTo != -1 {
// if there is only a to-node // if there is only a to-node
toName := fmt.Sprintf("n%v", relation.EntityTo) toName := fmt.Sprintf("n%v", relation.EntityTo)
ret += *createNodeLet(&jsonQuery.Entities[relation.EntityTo], &toName)
// Check if this entity index exists
if entity := &jsonQuery.Entities[relation.EntityTo]; entity != nil {
ret += *createNodeLet(entity, &toName)
}
ret += *createRelationLetWithOnlyToEntity(&relation, relationName, &jsonQuery.Entities, jsonQuery.Limit) ret += *createRelationLetWithOnlyToEntity(&relation, relationName, &jsonQuery.Entities, jsonQuery.Limit)
// Add this relation to the list // Add this relation to the list
......
package convertquery package convertquery
import ( import (
"errors"
"strings"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestMock(t *testing.T) { func TestEmptyQueryConversion(t *testing.T) {
// Setup for test
// Create query conversion service
service := NewService()
// s := `{"Return":{"Entities":[0,1],"Relations":[0]},"Entities":[{"Type":"airports","Constraints":[{"Attribute":"country","Value":"USA","DataType":"text","MatchType":"exact"}]},{"Type":"airports","Constraints":[{"Attribute":"city","Value":"New York","DataType":"text","MatchType":"exact"},{"Attribute":"vip","Value":"true","DataType":"bool","MatchType":"exact"}]}],"Relations":[{"Type":"flights","Depth":{"min":1,"max":1},"EntityFrom":0,"EntityTo":1,"Constraints":[{"Attribute":"Month","Value":"1","DataType":"number","MatchType":"exact"},{"Attribute":"Day","Value":"15","DataType":"number","MatchType":"exact"}]}]}` query := []byte(`{
"return": {
"entities": [],
"relations": []
},
"entities": [],
"relations": [],
"limit": 5000
}`)
// s3 := []byte(s) convertedResult, err := service.ConvertQuery(&query)
// // Convert the json byte msg to a query string // Assert that there is no error
// convertQueryService := NewService() assert.NoError(t, err)
// query, err := convertQueryService.ConvertQuery(&s3)
// if err != nil {
// errorhandler.LogError(err, "failed to parse incoming msg to query language") // TODO: send error message to client
// return
// }
// fmt.Println("Query: " + *query)
// // Make request to database // Assert that the result and the expected result are the same
// // TODO : Generate database seperately correctConvertedResult := `
// // execute and retrieve result LET nodes = first(RETURN UNION_DISTINCT([],[]))
// // convert result to general (node-link (?)) format LET edges = first(RETURN UNION_DISTINCT([],[]))
// requestService := request.NewService() RETURN {"vertices":nodes, "edges":edges }`
// result, err := requestService.SendAQLQuery(*query) assert.Equal(t, correctConvertedResult, *convertedResult)
// if err != nil { }
// logger.Log(err.Error())
// return // TODO: Send message in queue notifying of error func TestEntityOneAttributeQuery(t *testing.T) {
// } // Setup for test
// Create query conversion service
service := NewService()
query := []byte(`{
"return": {
"entities": [
0
],
"relations": []
},
"entities": [
{
"type": "airports",
"constraints": [
{
"attribute": "state",
"value": "HI",
"dataType": "text",
"matchType": "exact"
}
]
}
],
"relations": [],
"limit": 5000
}`)
convertedResult, err := service.ConvertQuery(&query)
// Assert that there is no error
assert.NoError(t, err)
// Assert that the result and the expected result are the same
correctConvertedResult := `LET n0 = (FOR x IN airports FILTER x.state == "HI" RETURN x)LET nodes = first(RETURN UNION_DISTINCT(n0,[],[]))LET edges = first(RETURN UNION_DISTINCT([],[]))RETURN {"vertices":nodes, "edges":edges }`
cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "")
cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "")
assert.Equal(t, correctConvertedResult, cleanedResult)
}
func TestRelationWithConstraint(t *testing.T) {
// Setup for test
// Create query conversion service
service := NewService()
query := []byte(`{
"return": {
"entities": [
0
],
"relations": [
0
]
},
"entities": [
{
"type": "airports",
"constraints": [
{
"attribute": "state",
"value": "HI",
"dataType": "text",
"matchType": "exact"
}
]
}
],
"relations": [
{
"type": "flights",
"depth": {
"min": 1,
"max": 1
},
"entityFrom": 0,
"entityTo": -1,
"constraints": [
{
"attribute": "Day",
"value": "15",
"dataType": "number",
"matchType": "EQ"
}
]
}
],
"limit": 5000
}`)
convertedResult, err := service.ConvertQuery(&query)
// Assert that there is no error
assert.NoError(t, err)
// Assert that the result and the expected result are the same
correctConvertedResult := `LET n0 = (FOR x IN airports FILTER x.state == "HI" RETURN x)LET r0 = (FOR x IN n0 FOR v, e, p IN 1..1 OUTBOUND x flights OPTIONS { uniqueEdges: "path" }FILTER p.edges[*].Day ALL == 15 LIMIT 5000 RETURN DISTINCT p )LET nodes = first(RETURN UNION_DISTINCT(flatten(r0[**].vertices), [],[]))LET edges = first(RETURN UNION_DISTINCT(flatten(r0[**].edges), [],[]))RETURN {"vertices":nodes, "edges":edges }`
cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "")
cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "")
assert.Equal(t, correctConvertedResult, cleanedResult)
}
func TestRelationWithInOutConstraint(t *testing.T) {
// Setup for test
// Create query conversion service
service := NewService()
query := []byte(`{
"return": {
"entities": [
0,
1
],
"relations": [
0
]
},
"entities": [
{
"type": "airports",
"constraints": [
{
"attribute": "city",
"value": "San Francisco",
"dataType": "text",
"matchType": "exact"
}
]
},
{
"type": "airports",
"constraints": [
{
"attribute": "state",
"value": "HI",
"dataType": "text",
"matchType": "exact"
}
]
}
],
"relations": [
{
"type": "flights",
"depth": {
"min": 1,
"max": 3
},
"entityFrom": 1,
"entityTo": 0,
"constraints": [
{
"attribute": "Day",
"value": "15",
"dataType": "number",
"matchType": "EQ"
}
]
}
],
"limit": 5000
}`)
convertedResult, err := service.ConvertQuery(&query)
// Assert that there is no error
assert.NoError(t, err)
// Assert that the result and the expected result are the same
correctConvertedResult := `LET n1 = (FOR x IN airports FILTER x.state == "HI" RETURN x)LET r0 = (FOR x IN n1 FOR v, e, p IN 1..3 OUTBOUND x flights OPTIONS { uniqueEdges: "path" }FILTER v.city == "San Francisco" FILTER p.edges[*].Day ALL == 15 LIMIT 5000 RETURN DISTINCT p )LET nodes = first(RETURN UNION_DISTINCT(flatten(r0[**].vertices), [],[]))LET edges = first(RETURN UNION_DISTINCT(flatten(r0[**].edges), [],[]))RETURN {"vertices":nodes, "edges":edges }`
cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "")
cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "")
assert.Equal(t, correctConvertedResult, cleanedResult)
}
func TestTwoRelations(t *testing.T) {
// Setup for test
// Create query conversion service
service := NewService()
query := []byte(`{
"return": {
"entities": [
0,
1,
2
],
"relations": [
0,
1
]
},
"entities": [
{
"type": "airports",
"constraints": [
{
"attribute": "city",
"value": "New York",
"dataType": "text",
"matchType": "exact"
}
]
},
{
"type": "airports",
"constraints": [
{
"attribute": "city",
"value": "San Francisco",
"dataType": "text",
"matchType": "exact"
}
]
},
{
"type": "airports",
"constraints": [
{
"attribute": "state",
"value": "HI",
"dataType": "text",
"matchType": "exact"
}
]
}
],
"relations": [
{
"type": "flights",
"depth": {
"min": 1,
"max": 3
},
"entityFrom": 2,
"entityTo": 1,
"constraints": [
{
"attribute": "Day",
"value": "15",
"dataType": "number",
"matchType": "EQ"
}
]
},
{
"type": "flights",
"depth": {
"min": 1,
"max": 1
},
"entityFrom": 0,
"entityTo": -1,
"constraints": []
}
],
"limit": 5000
}`)
convertedResult, err := service.ConvertQuery(&query)
// Assert that there is no error
assert.NoError(t, err)
// Assert that the result and the expected result are the same
correctConvertedResult := `LET n2 = (FOR x IN airports FILTER x.state == "HI" RETURN x)LET r0 = (FOR x IN n2 FOR v, e, p IN 1..3 OUTBOUND x flights OPTIONS { uniqueEdges: "path" }FILTER v.city == "San Francisco" FILTER p.edges[*].Day ALL == 15 LIMIT 5000 RETURN DISTINCT p )LET n0 = (FOR x IN airports FILTER x.city == "New York" RETURN x)LET r1 = (FOR x IN n0 FOR v, e, p IN 1..1 OUTBOUND x flights OPTIONS { uniqueEdges: "path" }LIMIT 5000 RETURN DISTINCT p )LET nodes = first(RETURN UNION_DISTINCT(flatten(r0[**].vertices), flatten(r1[**].vertices), [],[]))LET edges = first(RETURN UNION_DISTINCT(flatten(r0[**].edges), flatten(r1[**].edges), [],[]))RETURN {"vertices":nodes, "edges":edges }`
cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "")
cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "")
assert.Equal(t, correctConvertedResult, cleanedResult)
}
func TestRelationWithOnlyToNode(t *testing.T) {
// Setup for test
// Create query conversion service
service := NewService()
query := []byte(`{
"return": {
"entities": [
0
],
"relations": [
0
]
},
"entities": [
{
"type": "airports",
"constraints": [
{
"attribute": "city",
"value": "San Francisco",
"dataType": "text",
"matchType": "exact"
}
]
}
],
"relations": [
{
"type": "flights",
"depth": {
"min": 1,
"max": 1
},
"entityFrom": -1,
"entityTo": 0,
"constraints": []
}
],
"limit": 5000
}`)
convertedResult, err := service.ConvertQuery(&query)
// Assert that there is no error
assert.NoError(t, err)
// Assert that the result and the expected result are the same
correctConvertedResult := `LET n0 = (FOR x IN airports FILTER x.city == "San Francisco" RETURN x)LET r0 = (FOR x IN n0 FOR v, e, p IN 1..1 INBOUND x flights OPTIONS { uniqueEdges: "path" }LIMIT 5000 RETURN DISTINCT p )LET nodes = first(RETURN UNION_DISTINCT(flatten(r0[**].vertices), [],[]))LET edges = first(RETURN UNION_DISTINCT(flatten(r0[**].edges), [],[]))RETURN {"vertices":nodes, "edges":edges }`
cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "")
cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "")
assert.Equal(t, correctConvertedResult, cleanedResult)
}
func TestTooManyReturnEntities(t *testing.T) {
// Setup for test
// Create query conversion service
service := NewService()
query := []byte(`{
"return": {
"entities": [
0,
1,
2
],
"relations": [
0
]
},
"entities": [
{
"type": "airports",
"constraints": [
{
"attribute": "city",
"value": "San Francisco",
"dataType": "text",
"matchType": "exact"
}
]
}
],
"relations": [
{
"type": "flights",
"depth": {
"min": 1,
"max": 1
},
"entityFrom": -1,
"entityTo": 0,
"constraints": []
}
],
"limit": 5000
}`)
_, err := service.ConvertQuery(&query)
// Assert that there is no error
assert.Equal(t, errors.New("non-existing entity referenced in return"), err)
}
// fmt.Print("QueryResult: ") func TestTooManyReturnRelations(t *testing.T) {
// fmt.Println(*result) // Setup for test
// Create query conversion service
service := NewService()
assert.True(t, true, true) query := []byte(`{
"return": {
"entities": [
0
],
"relations": [
0,
1,
2
]
},
"entities": [
{
"type": "airports",
"constraints": [
{
"attribute": "city",
"value": "San Francisco",
"dataType": "text",
"matchType": "exact"
}
]
}
],
"relations": [
{
"type": "flights",
"depth": {
"min": 1,
"max": 1
},
"entityFrom": -1,
"entityTo": 0,
"constraints": []
}
],
"limit": 5000
}`)
_, err := service.ConvertQuery(&query)
// Assert that there is no error
assert.Equal(t, errors.New("non-existing relation referenced in return"), err)
}
func TestNegativeReturnEntities(t *testing.T) {
// Setup for test
// Create query conversion service
service := NewService()
query := []byte(`{
"return": {
"entities": [
0,
-1
],
"relations": [
0,
1,
2
]
},
"entities": [
{
"type": "airports",
"constraints": [
{
"attribute": "city",
"value": "San Francisco",
"dataType": "text",
"matchType": "exact"
}
]
}
],
"relations": [
{
"type": "flights",
"depth": {
"min": 1,
"max": 1
},
"entityFrom": -1,
"entityTo": 0,
"constraints": []
}
],
"limit": 5000
}`)
_, err := service.ConvertQuery(&query)
// Assert that there is no error
assert.Equal(t, errors.New("non-existing entity referenced in return"), err)
} }
// func TestHugeQuery(t *testing.T) { func TestNoRelationsField(t *testing.T) {
// Setup for test
// s := `{ // Create query conversion service
// "Return": { service := NewService()
// "Entities": [
// 0, query := []byte(`{
// 1 "return": {
// ], "entities": [
// "Relations": [ 0
// 0 ]
// ] },
// }, "entities": [
// "Entities": [ {
// { "type": "airports",
// "Type": "airports", "constraints": [
// "Constraints": [ {
// { "attribute": "city",
// "Attribute": "country", "value": "San Francisco",
// "Value": "USA", "dataType": "text",
// "DataType": "text", "matchType": "exact"
// "MatchType": "exact" }
// } ]
// ] }
// }, ],
// { "limit": 5000
// "Type": "airports", }`)
// "Constraints": [
// { convertedResult, err := service.ConvertQuery(&query)
// "Attribute": "city",
// "Value": "New York", // Assert that there is no error
// "DataType": "text", assert.NoError(t, err)
// "MatchType": "exact"
// }, // Assert that the result and the expected result are the same
// { correctConvertedResult := `LET n0 = (FOR x IN airports FILTER x.city == "San Francisco" RETURN x)LET nodes = first(RETURN UNION_DISTINCT(n0,[],[]))LET edges = first(RETURN UNION_DISTINCT([],[]))RETURN {"vertices":nodes, "edges":edges }`
// "Attribute": "vip", cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "")
// "Value": "true", cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "")
// "DataType": "bool", assert.Equal(t, correctConvertedResult, cleanedResult)
// "MatchType": "exact" }
// }
// ]
// }
// ],
// "Relations": [
// {
// "Type": "flights",
// "Depth": {
// "min": 1,
// "max": 1
// },
// "EntityFrom": 0,
// "EntityTo": 1,
// "Constraints": [
// {
// "Attribute": "Month",
// "Value": "1",
// "DataType": "number",
// "MatchType": "exact"
// },
// {
// "Attribute": "Day",
// "Value": "15",
// "DataType": "number",
// "MatchType": "exact"
// }
// ]
// }
// ]
// }`
// s3 := []byte(s)
// convertQueryService := NewService()
// j, _ := convertQueryService.ConvertQuery(&s3)
// expected := `LET n0 = (
// FOR x IN airports
// FILTER x.country == "USA"
// RETURN x
// )
// LET r0 = (
// FOR x IN n0
// FOR v, e, p IN 1..1 OUTBOUND x flights
// OPTIONS { uniqueEdges: "path" }
// FILTER v.city == "New York"
// AND v.vip == true
// FILTER p.edges[*].Month ALL == 1
// AND p.edges[*].Day ALL == 15
// LIMIT 1000
// RETURN DISTINCT p )
// LET nodes = first(RETURN UNION_DISTINCT(flatten(r0[**].vertices), [],[]))
// LET edges = first(RETURN UNION_DISTINCT(flatten(r0[**].edges), [],[]))
// RETURN {"vertices":nodes, "edges":edges }`
// assert.Equal(t, *j, expected)
// }
// func TestOnlyEntitiesQuery(t *testing.T) {
// s := `{
// "Return": {
// "Entities": [
// 0
// ],
// "Relations": []
// },
// "Entities": [
// {
// "Type": "airports",
// "Constraints": [
// {
// "Attribute": "city",
// "Value": "New York",
// "DataType": "text",
// "MatchType": "exact"
// },
// {
// "Attribute": "country",
// "Value": "USA",
// "DataType": "text",
// "MatchType": "exact"
// }
// ]
// }
// ],
// "Relations": []
// }`
// s3 := []byte(s)
// convertQueryService := NewService()
// j, _ := convertQueryService.ConvertQuery(&s3)
// expected := `LET n0 = (
// FOR x IN airports
// FILTER x.city == "New York"
// AND x.country == "USA"
// RETURN x
// )
// LET nodes = first(RETURN UNION_DISTINCT(n0,[],[]))
// LET edges = first(RETURN UNION_DISTINCT([],[]))
// RETURN {"vertices":nodes, "edges":edges }`
// assert.Equal(t, expected, *j)
// }
// func TestInboundQuery(t *testing.T) {
// s := `{
// "Return": {
// "Entities": [
// 0
// ],
// "Relations": [
// 0
// ]
// },
// "Entities": [
// {
// "Type": "airports",
// "Constraints": [
// {
// "Attribute": "city",
// "Value": "New York",
// "DataType": "text",
// "MatchType": "exact"
// }
// ]
// }
// ],
// "Relations": [
// {
// "Type": "flights",
// "Depth": {
// "min": 1,
// "max": 1
// },
// "EntityFrom": -1,
// "EntityTo": 0,
// "Constraints": [{
// "Attribute": "Day",
// "Value": "15",
// "DataType": "number",
// "MatchType": "exact"
// }]
// }
// ]
// }`
// s3 := []byte(s)
// convertQueryService := NewService()
// j, _ := convertQueryService.ConvertQuery(&s3)
// expected := `LET n0 = (
// FOR x IN airports
// FILTER x.city == "New York"
// RETURN x
// )
// LET r0 = (
// FOR x IN n0
// FOR v, e, p IN 1..1 INBOUND x flights
// OPTIONS { uniqueEdges: "path" }
// FILTER p.edges[*].Day ALL == 15
// LIMIT 1000
// RETURN DISTINCT p )
// LET nodes = first(RETURN UNION_DISTINCT(flatten(r0[**].vertices), [],[]))
// LET edges = first(RETURN UNION_DISTINCT(flatten(r0[**].edges), [],[]))
// RETURN {"vertices":nodes, "edges":edges }`
// assert.Equal(t, expected, *j)
// }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment