Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[main < T647] Add correctness testing for path output #355

Merged
merged 47 commits into from
Oct 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
de3e42b
add initial structure
Aug 2, 2023
8a0816a
implement date.parse
Aug 3, 2023
bc74ca0
remove cpp date module
Aug 3, 2023
1f3de7e
rename python date module
Aug 3, 2023
d3bae6b
remove cpp date module
Aug 3, 2023
7653423
add date.format draft
Aug 3, 2023
92360ea
add e2e date.format tests
Aug 4, 2023
f322d38
implement label.exists and e2e tests for it
Aug 5, 2023
39bec4e
add missing endline
Aug 5, 2023
5b41866
edit wrong procedure call
Aug 5, 2023
eb487ab
edit by review comments
Aug 7, 2023
84464b4
edit typing error
Aug 8, 2023
5a863a3
format files with black
Aug 9, 2023
71c6435
Merge branch 'main' into T585-T586-MAGE-implement-date
antoniofilipovic Aug 10, 2023
995fe4d
Merge branch 'main' into T585-T586-MAGE-implement-date
imilinovic Aug 11, 2023
cdd5071
Merge branch 'main' into T585-T586-MAGE-implement-date
antoniofilipovic Aug 11, 2023
07ed16d
Merge branch 'main' into T510-MAGE-implement-label
ind1xa Aug 11, 2023
158919a
Merge pull request #304 from memgraph/T510-MAGE-implement-label
antepusic Aug 16, 2023
456a7ba
Merge branch 'main' into T585-T586-MAGE-implement-date
ind1xa Aug 16, 2023
e59093a
Merge pull request #291 from memgraph/T585-T586-MAGE-implement-date
antepusic Aug 17, 2023
c39f0cd
merge
mpintaric55334 Aug 23, 2023
2aca436
Merge branch 'main' of https://github.com/memgraph/mage
mpintaric55334 Aug 28, 2023
731774f
Pull
mpintaric55334 Aug 31, 2023
fa5f222
Merge branch 'main' of https://github.com/memgraph/mage
mpintaric55334 Aug 31, 2023
0cc8e82
Merge branch 'main' of https://github.com/memgraph/mage
mpintaric55334 Sep 1, 2023
7b334da
Merge branch 'main' of https://github.com/memgraph/mage
mpintaric55334 Sep 4, 2023
6174dbf
Merge branch 'main' of https://github.com/memgraph/mage
mpintaric55334 Sep 4, 2023
c4277cc
Merge branch 'main' of https://github.com/memgraph/mage
mpintaric55334 Sep 5, 2023
128a7b9
Merge branch 'main' of https://github.com/memgraph/mage
mpintaric55334 Sep 6, 2023
991c1c4
Merge branch 'main' of https://github.com/memgraph/mage
mpintaric55334 Sep 10, 2023
4cb41e3
Merge branch 'main' of https://github.com/memgraph/mage
mpintaric55334 Sep 12, 2023
55071c6
Implement path correctness test
mpintaric55334 Sep 14, 2023
7d43c70
Black formatting
mpintaric55334 Sep 14, 2023
31a914e
CHanges to testing
mpintaric55334 Sep 20, 2023
bede219
Black
mpintaric55334 Sep 20, 2023
9157990
PR changes
mpintaric55334 Sep 20, 2023
e001987
Merge branch 'main' into T-add-testing-for-paths
mpintaric55334 Sep 20, 2023
4ccf814
CHanges
mpintaric55334 Sep 21, 2023
443ac7f
Empty line
mpintaric55334 Sep 21, 2023
61f215b
CHanges
mpintaric55334 Sep 21, 2023
84b3e25
PR changes
mpintaric55334 Oct 3, 2023
dfdd4ef
Merge branch 'main' into T-add-testing-for-paths
mpintaric55334 Oct 3, 2023
404cb95
Update e2e_correctness/test_modules.py
mpintaric55334 Oct 11, 2023
9d09ac5
Update e2e_correctness/test_modules.py
mpintaric55334 Oct 11, 2023
8283147
Merge branch 'main' into T-add-testing-for-paths
mpintaric55334 Oct 19, 2023
5a713ad
Merge branch 'main' into T-add-testing-for-paths
antoniofilipovic Oct 21, 2023
5785ab0
Merge branch 'main' into T-add-testing-for-paths
antoniofilipovic Oct 23, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions e2e_correctness/conftest.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@


def pytest_addoption(parser):
parser.addoption("--memgraph-port", type=int, action="store")
parser.addoption("--neo4j-port", type=int, action="store")
2 changes: 2 additions & 0 deletions e2e_correctness/path_test/test_path_expand1/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
path_option: >
True
7 changes: 7 additions & 0 deletions e2e_correctness/path_test/test_path_expand1/input.cyp
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
CREATE (d:Dog {name: "Rex", id: 0})-[h:HUNTS {id: 0}]->(c:Cat {name: "Tom", id: 1})-[catc:CATCHES {id:1}]->(m:Mouse {name: "Squiggles", id: 2});
MATCH (d:Dog) CREATE (d)-[bff:BEST_FRIENDS {id:2}]->(c:Cat {name: "Rinko", id: 3});
MATCH (c:Cat {name: "Tom"}) CREATE (h:Human {name: "Matija", id: 4})-[o:OWNS {id:3}]->(c);
MATCH (d:Dog {name: "Rex"}),(h:Human {name:"Matija"}) CREATE (h)-[o:PLAYS_WITH {id:4}]->(d);
MATCH (d:Dog {name: "Rex"}) CREATE (d)-[l:LIVES {id:5}]->(z:Zadar {id: 5});
MATCH (m:Mouse {name: "Squiggles"}), (z:Zadar) CREATE (m)-[r:RUNS_THROUGH {id:6}]->(z);
MATCH (z:Zadar) CREATE (h:Human {name: "Dena", id: 6})-[g:GOES_TO {id:7}]->(z);
8 changes: 8 additions & 0 deletions e2e_correctness/path_test/test_path_expand1/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
memgraph_query: >
MATCH (d:Dog), (h:Human)
CALL path.expand([d,id(h)],[],[],1,10) YIELD result RETURN result;


neo4j_query: >
MATCH (d:Dog), (h:Human)
CALL apoc.path.expand([d,h],"","",1,10) YIELD path RETURN path;
86 changes: 85 additions & 1 deletion e2e_correctness/query_neo_mem.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,11 @@ def __eq__(self, other):

class Edge:
def __init__(
self, from_vertex: int, to_vertex: int, label: str, properties: Dict[str, Any]
self,
from_vertex: int,
to_vertex: int,
label: str,
properties: Dict[str, Any],
):
self._from_vertex = from_vertex
self._to_vertex = to_vertex
Expand Down Expand Up @@ -297,3 +301,83 @@ def neo4j_get_graph(neo4j_driver: neo4j.BoltDriver) -> Graph:
json_data = get_neo4j_data_json(neo4j_driver)
logger.debug("Building the graph from Neo4j JSON data")
return create_graph_neo4j_json(json_data)


# additions for path testing
def sort_dict(dict):
keys = list(dict.keys())
keys.sort()
sorted_dict = {i: dict[i] for i in keys}
return sorted_dict


def execute_query_neo4j(driver: neo4j.BoltDriver, query: str) -> list:
with driver.session() as session:
query = neo4j.Query(query)
results = session.run(query).value()
return results


def path_to_string_neo4j(path): #type should be neo4j.graph.path but it doesnt recognize it in the definition
path_string_list = ["PATH: "]

n = len(path.nodes)

for i in range(0, n):
node = path.nodes[i]
node_labels = list(node.labels)
node_labels.sort()
sorted_dict = sort_dict(node._properties)
if "id" in sorted_dict:
sorted_dict.pop("id")
node_props = str(sorted_dict)
path_string_list.append(f"(id:{str(node.get('id'))} labels: {str(node_labels)} {node_props})-")

if i == n - 1:
path_string = "".join(path_string_list)
return path_string[:-1]

relationship = path.relationships[i]
sorted_dict_rel = sort_dict(relationship._properties)
if "id" in sorted_dict_rel:
sorted_dict_rel.pop("id")
rel_props = str(sorted_dict_rel)
path_string_list.append(f"[id:{str(relationship.get('id'))} type: {relationship.type} {str(rel_props)}]-")

def parse_neo4j(results: list) -> List[str]:
paths = [path_to_string_neo4j(res) for res in results]
paths.sort()
return paths


def path_to_string_mem(path: gqlalchemy.Path) -> str:
path_string_list = ["PATH: "]

n = len(path._nodes)

for i in range(0, n):
node = path._nodes[i]
node_labels = list(node._labels)
node_labels.sort()
sorted_dict = sort_dict(node._properties)
if "id" in sorted_dict:
sorted_dict.pop("id")
node_props = str(sorted_dict)
path_string_list.append(f"(id:{str(node._properties.get('id'))} labels: {str(node_labels)} {str(node_props)})-")

if i == n - 1:
path_string = "".join(path_string_list)
return path_string[:-1]

relationship = path._relationships[i]
sorted_dict_rel = sort_dict(relationship._properties)
if "id" in sorted_dict_rel:
sorted_dict_rel.pop("id")
rel_props = str(sorted_dict_rel)
path_string_list.append(f"[id:{str(relationship._properties.get('id'))} type: {relationship._type} {str(rel_props)}]-")


def parse_mem(results:list) -> List[str]:
paths = [path_to_string_mem(result["result"]) for result in results]
paths.sort()
return paths
81 changes: 75 additions & 6 deletions e2e_correctness/test_modules.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import neo4j
import pytest
import yaml
import os


from gqlalchemy import Memgraph
Expand All @@ -25,6 +26,11 @@
neo4j_get_graph,
run_memgraph_query,
run_neo4j_query,
execute_query_neo4j,
path_to_string_neo4j,
parse_neo4j,
path_to_string_mem,
parse_mem,
)

logging.basicConfig(format="%(asctime)-15s [%(levelname)s]: %(message)s")
Expand All @@ -49,6 +55,8 @@ class TestConstants:
TEST_FILE = "test.yml"
MEMGRAPH_QUERY = "memgraph_query"
NEO4J_QUERY = "neo4j_query"
CONFIG_FILE = "config.yml"



class ConfigConstants:
Expand Down Expand Up @@ -79,7 +87,9 @@ def get_all_tests():
if not test_or_group_dir.is_dir():
continue

if test_or_group_dir.name.endswith(TestConstants.TEST_GROUP_DIR_SUFFIX):
if test_or_group_dir.name.endswith(
TestConstants.TEST_GROUP_DIR_SUFFIX
):
for test_dir in test_or_group_dir.iterdir():
if not test_dir.is_dir():
continue
Expand Down Expand Up @@ -143,11 +153,15 @@ def _graphs_equal(memgraph_graph: Graph, neo4j_graph: Graph) -> bool:
return True


def _run_test(test_dir: str, memgraph_db: Memgraph, neo4j_driver: neo4j.BoltDriver):
def _run_test(
test_dir: str, memgraph_db: Memgraph, neo4j_driver: neo4j.BoltDriver
) -> None:
"""
Run input queries on Memgraph and Neo4j and compare graphs after running test query
"""
input_cyphers = test_dir.joinpath(TestConstants.INPUT_FILE).open("r").readlines()
input_cyphers = (
test_dir.joinpath(TestConstants.INPUT_FILE).open("r").readlines()
)
mg_execute_cyphers(input_cyphers, memgraph_db)
logger.info(f"Imported data into Memgraph from {input_cyphers}")
neo4j_execute_cyphers(input_cyphers, neo4j_driver)
Expand All @@ -162,7 +176,9 @@ def _run_test(test_dir: str, memgraph_db: Memgraph, neo4j_driver: neo4j.BoltDriv
run_memgraph_query(test_dict[TestConstants.MEMGRAPH_QUERY], memgraph_db)
logger.info("Done")

logger.info(f"Running query against Neo4j: {test_dict[TestConstants.NEO4J_QUERY]}")
logger.info(
f"Running query against Neo4j: {test_dict[TestConstants.NEO4J_QUERY]}"
)
run_neo4j_query(test_dict[TestConstants.NEO4J_QUERY], neo4j_driver)
logger.info("Done")

Expand All @@ -174,11 +190,57 @@ def _run_test(test_dir: str, memgraph_db: Memgraph, neo4j_driver: neo4j.BoltDriv
), "The graphs are not equal, check the logs for more details"


def _run_path_test(
test_dir: str, memgraph_db: Memgraph, neo4j_driver: neo4j.BoltDriver
) -> None:
"""
Run input queries on Memgraph and Neo4j and compare path results after running test query
"""
input_cyphers = (
test_dir.joinpath(TestConstants.INPUT_FILE).open("r").readlines()
)
logger.info(f"Importing data from {input_cyphers}")
mg_execute_cyphers(input_cyphers, memgraph_db)
logger.info("Imported data into Memgraph")
neo4j_execute_cyphers(input_cyphers, neo4j_driver)
logger.info("Imported data into Neo4j")

test_dict = _load_yaml(test_dir.joinpath(TestConstants.TEST_FILE))
logger.info(f"Test dict {test_dict}")

logger.info(
f"Running query against Memgraph: {test_dict[TestConstants.MEMGRAPH_QUERY]}"
)
memgraph_results = memgraph_db.execute_and_fetch(
test_dict[TestConstants.MEMGRAPH_QUERY]
)
memgraph_paths = parse_mem(memgraph_results)
logger.info("Done")

logger.info(
f"Running query against Neo4j: {test_dict[TestConstants.NEO4J_QUERY]}"
)
neo4j_results = execute_query_neo4j(
neo4j_driver, test_dict[TestConstants.NEO4J_QUERY]
)
neo4j_paths = parse_neo4j(neo4j_results)
logger.info("Done")

assert memgraph_paths == neo4j_paths

def check_path_option(test_dir):
antoniofilipovic marked this conversation as resolved.
Show resolved Hide resolved
config_path = test_dir.joinpath(TestConstants.CONFIG_FILE)
if(os.path.exists(config_path)):
config_dict = _load_yaml(config_path)
if "path_option" in config_dict:
option = config_dict["path_option"].strip()
return ( option == "True")
imilinovic marked this conversation as resolved.
Show resolved Hide resolved
return False
@pytest.fixture(scope="session")
def memgraph_port(pytestconfig):
return pytestconfig.getoption("--memgraph-port")


@pytest.fixture(scope="session", autouse=True)
def memgraph_db(memgraph_port):
memgraph_db = create_memgraph_db(memgraph_port)
Expand All @@ -191,6 +253,7 @@ def memgraph_db(memgraph_port):
def neo4j_port(pytestconfig):
return pytestconfig.getoption("--neo4j-port")


@pytest.fixture(scope="session", autouse=True)
def neo4j_driver(neo4j_port):
neo4j_driver = create_neo4j_driver(neo4j_port)
Expand All @@ -201,15 +264,21 @@ def neo4j_driver(neo4j_port):

@pytest.mark.parametrize("test_dir", tests)
def test_end2end(
test_dir: Path, memgraph_db: Memgraph, neo4j_driver: neo4j.BoltDriver
test_dir: Path,
memgraph_db: Memgraph,
neo4j_driver: neo4j.BoltDriver,
):
logger.debug("Dropping the Memgraph and Neo4j databases.")

clean_memgraph_db(memgraph_db)
clean_neo4j_db(neo4j_driver)

if test_dir.name.startswith(TestConstants.TEST_SUBDIR_PREFIX):
_run_test(test_dir, memgraph_db, neo4j_driver)

if check_path_option(test_dir):
_run_path_test(test_dir, memgraph_db, neo4j_driver)
else:
_run_test(test_dir, memgraph_db, neo4j_driver)
else:
logger.info(f"Skipping directory: {test_dir.name}")

Expand Down
41 changes: 27 additions & 14 deletions test_e2e_correctness.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,26 @@ class ConfigConstants:
NEO4J_PORT = 7688
MEMGRAPH_PORT = 7687


def parse_arguments():
parser = argparse.ArgumentParser(
description="Test MAGE E2E correctness."
)
parser = argparse.ArgumentParser(description="Test MAGE E2E correctness.")
parser.add_argument(
"-k", help="Filter what tests you want to run", type=str, required=False
"-k",
help="Filter what tests you want to run",
type=str,
required=False,
)
parser.add_argument(
"--memgraph-port", help="Set the port that Memgraph is listening on", type=int, required=False
"--memgraph-port",
help="Set the port that Memgraph is listening on",
type=int,
required=False,
)
parser.add_argument(
"--neo4j-port", help="Set the port that Neo4j is listening on", type=int, required=False
"--neo4j-port",
help="Set the port that Neo4j is listening on",
type=int,
required=False,
)
args = parser.parse_args()
return args
Expand All @@ -34,18 +42,21 @@ def parse_arguments():
#################################################


def main(test_filter: str = None,
memgraph_port:str = str(ConfigConstants.MEMGRAPH_PORT),
neo4j_port: str = str(ConfigConstants.NEO4J_PORT)):
def main(
test_filter: str = None,
memgraph_port: str = str(ConfigConstants.MEMGRAPH_PORT),
neo4j_port: str = str(ConfigConstants.NEO4J_PORT),
):
os.environ["PYTHONPATH"] = E2E_CORRECTNESS_DIRECTORY
os.chdir(E2E_CORRECTNESS_DIRECTORY)
command = ["python3", "-m", "pytest", ".", "-vv"]
if test_filter:
command.extend(["-k", test_filter])


command.extend(["--memgraph-port", memgraph_port])
command.extend(["--neo4j-port", neo4j_port])


subprocess.run(command)


Expand All @@ -59,7 +70,9 @@ def main(test_filter: str = None,
memgraph_port = str(memgraph_port)
if neo4j_port:
neo4j_port = str(neo4j_port)

main(test_filter=test_filter,
memgraph_port=memgraph_port,
neo4j_port=neo4j_port)

main(
test_filter=test_filter,
memgraph_port=memgraph_port,
neo4j_port=neo4j_port,
)
Loading