Skip to content

Commit

Permalink
Merge branch 'main' into T-node-in-out-degree
Browse files Browse the repository at this point in the history
  • Loading branch information
mpintaric55334 authored Oct 24, 2023
2 parents a2fcdc4 + e56adcc commit 6bd8bde
Show file tree
Hide file tree
Showing 7 changed files with 204 additions and 23 deletions.
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):
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")
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,
)

0 comments on commit 6bd8bde

Please sign in to comment.