/*
This program has been developed by students from the bachelor Computer Science at Utrecht University within the Software Project course.
© Copyright Utrecht University (Department of Information and Computing Sciences)
*/

package aql

import (
	"encoding/json"
	"errors"
	"strings"
	"testing"

	"git.science.uu.nl/graphpolaris/query-conversion/entity"
	"github.com/stretchr/testify/assert"
)

/*
Tests an empty query
	t: *testing.T, makes go recognise this as a test
*/
func TestEmptyQueryConversion(t *testing.T) {
	// Setup for test
	// Create query conversion service
	service := NewService()

	query := []byte(`{
		"return": {
			"entities": [],
			"relations": []
		},
		"entities": [],
		"relations": [],
		"limit": 5000
	}`)

	// Unmarshall the incoming message into an IncomingJSONQuery object
	var JSONQuery entity.IncomingQueryJSON
	json.Unmarshal(query, &JSONQuery)

	convertedResult, err := service.ConvertQuery(&JSONQuery)

	// Assert that there is no error
	assert.NoError(t, err)

	// Assert that the result and the expected result are the same
	correctConvertedResult := `
LET nodes = first(RETURN UNION_DISTINCT([],[]))
LET edges = first(RETURN UNION_DISTINCT([],[]))
RETURN {"vertices":nodes, "edges":edges }`
	assert.Equal(t, correctConvertedResult, *convertedResult)
}

/*
Tests multiple entity types
	t: *testing.T, makes go recognise this as a test
*/
func TestMultipleEntityTypes(t *testing.T) {
	// Setup for test
	// Create query conversion service
	service := NewService()

	query := []byte(`{
		"databaseName": "test",
		"return": {
		  "entities": [
			0,
			1
		  ],
		  "relations": [
			0
		  ]
		},
		"entities": [
		  {
			"type": "kamerleden",
			"constraints": [
			  {
				"attribute": "partij",
				"value": "GL",
				"dataType": "text",
				"matchType": "exact"
			  }
			]
		  },
		  {
			"type": "partijen",
			"constraints": [
			  {
				"attribute": "zetels",
				"value": "6",
				"dataType": "number",
				"matchType": "GT"
			  }
			]
		  }
		],
		"relations": [
		  {
			"type": "lid_van",
			"depth": {
			  "min": 1,
			  "max": 1
			},
			"entityFrom": 0,
			"entityTo": 1,
			"constraints": []
		  }
		],
		"limit": 5000,
		"modifiers": []
	  }`)

	// Unmarshall the incoming message into an IncomingJSONQuery object
	var JSONQuery entity.IncomingQueryJSON
	json.Unmarshal(query, &JSONQuery)

	convertedResult, err := service.ConvertQuery(&JSONQuery)

	// Assert that there is no error
	assert.NoError(t, err)

	// Assert that the result and the expected result are the same
	correctConvertedResult := "WITH partijen\nLET n0 = (\n\tFOR x IN kamerleden \n\tFILTER x.partij == \"GL\" \n\tRETURN x\n)\nLET r0 = (\n\tFOR x IN n0 \n\tFOR v, e, p IN 1..1 OUTBOUND x lid_van \n\tOPTIONS { uniqueEdges: \"path\" }\n\tFILTER v.zetels > 6 \n\tLIMIT 5000 \nRETURN DISTINCT p )\n\nLET nodes = first(RETURN UNION_DISTINCT(flatten(r0[**].vertices), [],[]))\nLET edges = first(RETURN UNION_DISTINCT(flatten(r0[**].edges), [],[]))\nRETURN {\"vertices\":nodes, \"edges\":edges }"
	cleanedResult := strings.ReplaceAll(correctConvertedResult, "\n", "")
	cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "")
	convertedCleanedResult := strings.ReplaceAll(*convertedResult, "\n", "")
	convertedCleanedResult = strings.ReplaceAll(convertedCleanedResult, "\t", "")

	assert.Equal(t, convertedCleanedResult, cleanedResult)
}

/*
Tests a query with one attribute
	t: *testing.T, makes go recognise this as a test
*/
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": "string",
						"matchType": "exact"
					}
				]
			}
		],
		"relations": [],
		"limit": 5000
	}`)

	// Unmarshall the incoming message into an IncomingJSONQuery object
	var JSONQuery entity.IncomingQueryJSON
	json.Unmarshal(query, &JSONQuery)

	convertedResult, err := service.ConvertQuery(&JSONQuery)

	// 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)
}

/*
Test a relation with a constraint
	t: *testing.T, makes go recognise this as a test
*/
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": "string",
						"matchType": "exact"
					}
				]
			}
		],
		"relations": [
			{
				"type": "flights",
				"depth": {
					"min": 1,
					"max": 1
				},
				"entityFrom": 0,
				"entityTo": -1,
				"constraints": [
					{
						"attribute": "Day",
						"value": "15",
						"dataType": "int",
						"matchType": "EQ"
					}
				]
			}
		],
		"limit": 5000
	}`)

	// Unmarshall the incoming message into an IncomingJSONQuery object
	var JSONQuery entity.IncomingQueryJSON
	json.Unmarshal(query, &JSONQuery)

	convertedResult, err := service.ConvertQuery(&JSONQuery)

	// 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)
}

/*
Tests the count modifier
	t: *testing.T, makes go recognise this as a test
*/
func TestModifierCountEntity(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": "string",
						"matchType": "exact"
					}
				]
			}
		],
		"relations": [],
		"limit": 5000,
		"modifiers": [
			{
				"type": "COUNT",
				"selectedType": "entity",
				"id": 0,
				"attributeIndex": -1
			}
		]
	}`)

	// Unmarshall the incoming message into an IncomingJSONQuery object
	var JSONQuery entity.IncomingQueryJSON
	json.Unmarshal(query, &JSONQuery)

	convertedResult, err := service.ConvertQuery(&JSONQuery)

	// 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)RETURN LENGTH (n0)`
	cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "")
	cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "")
	assert.Equal(t, correctConvertedResult, cleanedResult)
}

/*
Tests the count modifer with an attribute
	t: *testing.T, makes go recognise this as a test
*/
func TestModifierCountEntityAttribute(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": "string",
						"matchType": "exact"
					}
				]
			}
		],
		"relations": [],
		"limit": 5000,
		"modifiers": [
			{
				"type": "SUM",
				"selectedType": "entity",
				"id": 0,
				"attributeIndex": 0
			}
		]
	}`)

	// Unmarshall the incoming message into an IncomingJSONQuery object
	var JSONQuery entity.IncomingQueryJSON
	json.Unmarshal(query, &JSONQuery)

	convertedResult, err := service.ConvertQuery(&JSONQuery)

	// 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)RETURN SUM (n0[*].state)`
	cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "")
	cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "")
	assert.Equal(t, correctConvertedResult, cleanedResult)
}

/*
Tests the count modifier on a relation
	t: *testing.T, makes go recognise this as a test
*/
func TestModifierCountRelation(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": "string",
						"matchType": "exact"
					}
				]
			}
		],
		"relations": [
			{
				"type": "flights",
				"depth": {
					"min": 1,
					"max": 1
				},
				"entityFrom": 0,
				"entityTo": -1,
				"constraints": [
					{
						"attribute": "Day",
						"value": "15",
						"dataType": "int",
						"matchType": "EQ"
					}
				]
			}
		],
		"limit": 5000,
		"modifiers": [
			{
				"type": "COUNT",
				"selectedType": "relation",
				"id": 0,
				"attributeIndex": -1
			}
		]
	}`)

	// Unmarshall the incoming message into an IncomingJSONQuery object
	var JSONQuery entity.IncomingQueryJSON
	json.Unmarshal(query, &JSONQuery)

	convertedResult, err := service.ConvertQuery(&JSONQuery)

	// 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 RETURN DISTINCT p )RETURN LENGTH (unique(r0[*].edges[**]))`
	cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "")
	cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "")
	assert.Equal(t, correctConvertedResult, cleanedResult)
}

/*
Tests the count modifier with an entity swap
	t: *testing.T, makes go recognise this as a test
*/
func TestModifierCountEntitySwap(t *testing.T) {
	// Setup for test
	// Create query conversion service
	service := NewService()

	query := []byte(`{
		"databaseName": "TweedeKamer",
		"return": {
		  "entities": [
			0,
			1
		  ],
		  "relations": [
			0
		  ]
		},
		"entities": [
		  {
			"type": "partijen",
			"constraints": []
		  },
		  {
			"type": "kamerleden",
			"constraints": []
		  }
		],
		"relations": [
		  {
			"type": "lid_van",
			"depth": {
			  "min": 1,
			  "max": 1
			},
			"entityFrom": 1,
			"entityTo": 0,
			"constraints": []
		  }
		],
		"limit": 5000,
		"modifiers": [
		  {
			"type": "COUNT",
			"selectedType": "entity",
			"selectedTypeId": 1,
			"attributeIndex": -1
		  }
		]
	  }`)

	// Unmarshall the incoming message into an IncomingJSONQuery object
	var JSONQuery entity.IncomingQueryJSON
	json.Unmarshal(query, &JSONQuery)
	convertedResult, err := service.ConvertQuery(&JSONQuery)

	// Assert that there is no error
	assert.NoError(t, err)

	// Assert that the result and the expected result are the same
	correctConvertedResult := `WITH partijenLET n1 = (FOR x IN kamerleden RETURN x)LET r0 = (FOR x IN n1 FOR v, e, p IN 1..1 OUTBOUND x lid_van OPTIONS { uniqueEdges: "path" }RETURN DISTINCT p )RETURN LENGTH (unique(r0[*].vertices[0]))`
	cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "")
	cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "")
	assert.Equal(t, correctConvertedResult, cleanedResult)
}

/*
Tests the count modifier on a relation and attribute
	t: *testing.T, makes go recognise this as a test
*/
func TestModifierCountRelationAttribute(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": "string",
						"matchType": "exact"
					}
				]
			}
		],
		"relations": [
			{
				"type": "flights",
				"depth": {
					"min": 1,
					"max": 1
				},
				"entityFrom": 0,
				"entityTo": -1,
				"constraints": [
					{
						"attribute": "Day",
						"value": "15",
						"dataType": "int",
						"matchType": "EQ"
					}
				]
			}
		],
		"limit": 5000,
		"modifiers": [
			{
				"type": "AVG",
				"selectedType": "relation",
				"id": 0,
				"attributeIndex": 0
			}
		]
	}`)

	// Unmarshall the incoming message into an IncomingJSONQuery object
	var JSONQuery entity.IncomingQueryJSON
	json.Unmarshal(query, &JSONQuery)

	convertedResult, err := service.ConvertQuery(&JSONQuery)

	// 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 RETURN DISTINCT p )RETURN AVG (r0[*].edges[**].Day)`
	cleanedResult := strings.ReplaceAll(*convertedResult, "\n", "")
	cleanedResult = strings.ReplaceAll(cleanedResult, "\t", "")
	assert.Equal(t, correctConvertedResult, cleanedResult)
}

/*
Tests a relation with an in out constraint
	t: *testing.T, makes go recognise this as a test
*/
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": "string",
						"matchType": "exact"
					}
				]
			},
			{
				"type": "airports",
				"constraints": [
					{
						"attribute": "state",
						"value": "HI",
						"dataType": "string",
						"matchType": "exact"
					}
				]
			}
		],
		"relations": [
			{
				"type": "flights",
				"depth": {
					"min": 1,
					"max": 3
				},
				"entityFrom": 1,
				"entityTo": 0,
				"constraints": [
					{
						"attribute": "Day",
						"value": "15",
						"dataType": "int",
						"matchType": "EQ"
					}
				]
			}
		],
		"limit": 5000
	}`)

	// Unmarshall the incoming message into an IncomingJSONQuery object
	var JSONQuery entity.IncomingQueryJSON
	json.Unmarshal(query, &JSONQuery)

	convertedResult, err := service.ConvertQuery(&JSONQuery)

	// 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)
}

/*
Tests two relations
	t: *testing.T, makes go recognise this as a test
*/
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": "string",
						"matchType": "exact"
					}
				]
			},
			{
				"type": "airports",
				"constraints": [
					{
						"attribute": "city",
						"value": "San Francisco",
						"dataType": "string",
						"matchType": "exact"
					}
				]
			},
			{
				"type": "airports",
				"constraints": [
					{
						"attribute": "state",
						"value": "HI",
						"dataType": "string",
						"matchType": "exact"
					}
				]
			}
		],
		"relations": [
			{
				"type": "flights",
				"depth": {
					"min": 1,
					"max": 3
				},
				"entityFrom": 2,
				"entityTo": 1,
				"constraints": [
					{
						"attribute": "Day",
						"value": "15",
						"dataType": "int",
						"matchType": "EQ"
					}
				]
			},
			{
				"type": "flights",
				"depth": {
					"min": 1,
					"max": 1
				},
				"entityFrom": 0,
				"entityTo": -1,
				"constraints": []
			}
		],
		"limit": 5000
	}`)

	// Unmarshall the incoming message into an IncomingJSONQuery object
	var JSONQuery entity.IncomingQueryJSON
	json.Unmarshal(query, &JSONQuery)

	convertedResult, err := service.ConvertQuery(&JSONQuery)

	// 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)
}

/*
Tests a relation with only a to node
	t: *testing.T, makes go recognise this as a test
*/
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": "string",
							"matchType": "exact"
						}
					]
				}
			],
			"relations": [
				{
					"type": "flights",
					"depth": {
						"min": 1,
						"max": 1
					},
					"entityFrom": -1,
					"entityTo": 0,
					"constraints": []
				}
			],
			"limit": 5000
		}`)

	// Unmarshall the incoming message into an IncomingJSONQuery object
	var JSONQuery entity.IncomingQueryJSON
	json.Unmarshal(query, &JSONQuery)

	convertedResult, err := service.ConvertQuery(&JSONQuery)

	// 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)
}

/*
Tests too manu return entities
	t: *testing.T, makes go recognise this as a test
*/
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": "string",
							"matchType": "exact"
						}
					]
				}
			],
			"relations": [
				{
					"type": "flights",
					"depth": {
						"min": 1,
						"max": 1
					},
					"entityFrom": -1,
					"entityTo": 0,
					"constraints": []
				}
			],
			"limit": 5000
		}`)

	// Unmarshall the incoming message into an IncomingJSONQuery object
	var JSONQuery entity.IncomingQueryJSON
	json.Unmarshal(query, &JSONQuery)

	_, err := service.ConvertQuery(&JSONQuery)

	// Assert that there is no error
	assert.Equal(t, errors.New("non-existing entity referenced in return"), err)
}

/*
Tests too manu return relations
	t: *testing.T, makes go recognise this as a test
*/
func TestTooManyReturnRelations(t *testing.T) {
	// Setup for test
	// Create query conversion service
	service := NewService()

	query := []byte(`{
			"return": {
				"entities": [
					0
				],
				"relations": [
					0,
					1,
					2
				]
			},
			"entities": [
				{
					"type": "airports",
					"constraints": [
						{
							"attribute": "city",
							"value": "San Francisco",
							"dataType": "string",
							"matchType": "exact"
						}
					]
				}
			],
			"relations": [
				{
					"type": "flights",
					"depth": {
						"min": 1,
						"max": 1
					},
					"entityFrom": -1,
					"entityTo": 0,
					"constraints": []
				}
			],
			"limit": 5000
		}`)

	// Unmarshall the incoming message into an IncomingJSONQuery object
	var JSONQuery entity.IncomingQueryJSON
	json.Unmarshal(query, &JSONQuery)

	_, err := service.ConvertQuery(&JSONQuery)

	// Assert that there is no error
	assert.Equal(t, errors.New("non-existing relation referenced in return"), err)
}

/*
Tests negative return entities
	t: *testing.T, makes go recognise this as a test
*/
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": "string",
							"matchType": "exact"
						}
					]
				}
			],
			"relations": [
				{
					"type": "flights",
					"depth": {
						"min": 1,
						"max": 1
					},
					"entityFrom": -1,
					"entityTo": 0,
					"constraints": []
				}
			],
			"limit": 5000
		}`)

	// Unmarshall the incoming message into an IncomingJSONQuery object
	var JSONQuery entity.IncomingQueryJSON
	json.Unmarshal(query, &JSONQuery)

	_, err := service.ConvertQuery(&JSONQuery)

	// Assert that there is no error
	assert.Equal(t, errors.New("non-existing entity referenced in return"), err)
}

/*
Tests a query with no relation field
	t: *testing.T, makes go recognise this as a test
*/
func TestNoRelationsField(t *testing.T) {
	// Setup for test
	// Create query conversion service
	service := NewService()

	query := []byte(`{
			"return": {
				"entities": [
					0
				]
			},
			"entities": [
				{
					"type": "airports",
					"constraints": [
						{
							"attribute": "city",
							"value": "San Francisco",
							"dataType": "string",
							"matchType": "exact"
						}
					]
				}
			],
			"limit": 5000
		}`)

	// Unmarshall the incoming message into an IncomingJSONQuery object
	var JSONQuery entity.IncomingQueryJSON
	json.Unmarshal(query, &JSONQuery)

	convertedResult, err := service.ConvertQuery(&JSONQuery)

	// 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 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)
}

/*
Tests a query with double WITH
	t: *testing.T, makes go recognise this as a test
*/
func TestDoubleWITH(t *testing.T) {
	// Setup for test
	// Create query conversion service
	service := NewService()

	query := []byte(`{
        "databaseName": "test",
        "return": {
          "entities": [
            0,
            1,
            2,
            3
          ],
          "relations": [
            0,
            1
          ]
        },
        "entities": [
          {
            "type": "kamerleden",
            "constraints": []
          },
          {
            "type": "partijen",
            "constraints": []
          }
          ,
          {
            "type": "kamerleden",
            "constraints": []
          },
          {
            "type": "commissies",
            "constraints": []
          }
        ],
        "relations": [
          {
            "type": "lid_van",
            "depth": {
              "min": 1,
              "max": 1
            },
            "entityFrom": 0,
            "entityTo": 1,
            "constraints": []
          },
          {
            "type": "onderdeel_van",
            "depth": {
              "min": 1,
              "max": 1
            },
            "entityFrom": 2,
            "entityTo": 3,
            "constraints": []
          }
        ],
        "limit": 5000,
        "modifiers": []
      }`)

	// Unmarshall the incoming message into an IncomingJSONQuery object
	var JSONQuery entity.IncomingQueryJSON
	json.Unmarshal(query, &JSONQuery)

	convertedResult, err := service.ConvertQuery(&JSONQuery)

	// Assert that there is no error
	assert.NoError(t, err)

	// Assert that the result and the expected result are the same
	correctConvertedResult := "WITH partijen, commissiesLET n0 = (FOR x IN kamerleden RETURN x)LET r0 = (FOR x IN n0 FOR v, e, p IN 1..1 OUTBOUND x lid_van OPTIONS { uniqueEdges: \"path\" }LIMIT 5000 RETURN DISTINCT p )LET n2 = (FOR x IN kamerleden RETURN x)LET r1 = (FOR x IN n2 FOR v, e, p IN 1..1 OUTBOUND x onderdeel_van 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)
}

/*
Tests an entity with a lower than -1 in a relation
	t: *testing.T, makes go recognise this as a test
*/
func TestEntityFromLowerThanNegativeOneInRelation(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": "string",
							"matchType": "exact"
						}
					]
				}
			],
			"relations": [
				{
					"type": "flights",
					"depth": {
						"min": 1,
						"max": 1
					},
					"entityFrom": -4,
					"entityTo": 0,
					"constraints": []
				}
			],
			"limit": 5000
		}`)

	// Unmarshall the incoming message into an IncomingJSONQuery object
	var JSONQuery entity.IncomingQueryJSON
	json.Unmarshal(query, &JSONQuery)

	_, err := service.ConvertQuery(&JSONQuery)

	// Assert that there is no error
	assert.NoError(t, err)
}