Add Python query module API mock (#757)
This commit is contained in:
parent
6abd356d01
commit
97d45ab1d8
343
include/_mgp_mock.py
Normal file
343
include/_mgp_mock.py
Normal file
@ -0,0 +1,343 @@
|
|||||||
|
import typing
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
import networkx as nx
|
||||||
|
|
||||||
|
NX_LABEL_ATTR = "labels"
|
||||||
|
NX_TYPE_ATTR = "type"
|
||||||
|
|
||||||
|
SOURCE_TYPE_KAFKA = "SOURCE_TYPE_KAFKA"
|
||||||
|
SOURCE_TYPE_PULSAR = "SOURCE_TYPE_PULSAR"
|
||||||
|
|
||||||
|
"""
|
||||||
|
This module provides helpers for the mock Python API, much like _mgp.py does for mgp.py.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidArgumentError(Exception):
|
||||||
|
"""
|
||||||
|
Signals that some of the arguments have invalid values.
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ImmutableObjectError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class LogicErrorError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class DeletedObjectError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class EdgeConstants(Enum):
|
||||||
|
I_START = 0
|
||||||
|
I_END = 1
|
||||||
|
I_KEY = 2
|
||||||
|
|
||||||
|
|
||||||
|
class Graph:
|
||||||
|
"""Wrapper around a NetworkX MultiDiGraph instance."""
|
||||||
|
|
||||||
|
__slots__ = ("nx", "_highest_vertex_id", "_highest_edge_id", "_valid")
|
||||||
|
|
||||||
|
def __init__(self, graph: nx.MultiDiGraph) -> None:
|
||||||
|
if not isinstance(graph, nx.MultiDiGraph):
|
||||||
|
raise TypeError(f"Expected 'networkx.classes.multidigraph.MultiDiGraph', got '{type(graph)}'")
|
||||||
|
|
||||||
|
self.nx = graph
|
||||||
|
self._highest_vertex_id = None
|
||||||
|
self._highest_edge_id = None
|
||||||
|
self._valid = True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def vertex_ids(self):
|
||||||
|
return self.nx.nodes
|
||||||
|
|
||||||
|
def vertex_is_isolate(self, vertex_id: int) -> bool:
|
||||||
|
return nx.is_isolate(self.nx, vertex_id)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def vertices(self):
|
||||||
|
return (Vertex(node_id, self) for node_id in self.nx.nodes)
|
||||||
|
|
||||||
|
def has_node(self, node_id):
|
||||||
|
return self.nx.has_node(node_id)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def edges(self):
|
||||||
|
return self.nx.edges
|
||||||
|
|
||||||
|
def is_valid(self) -> bool:
|
||||||
|
return self._valid
|
||||||
|
|
||||||
|
def get_vertex_by_id(self, vertex_id: int) -> "Vertex":
|
||||||
|
return Vertex(vertex_id, self)
|
||||||
|
|
||||||
|
def invalidate(self):
|
||||||
|
self._valid = False
|
||||||
|
|
||||||
|
def is_immutable(self) -> bool:
|
||||||
|
return nx.is_frozen(self.nx)
|
||||||
|
|
||||||
|
def make_immutable(self):
|
||||||
|
self.nx = nx.freeze(self.nx)
|
||||||
|
|
||||||
|
def _new_vertex_id(self):
|
||||||
|
if self._highest_vertex_id is None:
|
||||||
|
self._highest_vertex_id = max(vertex_id for vertex_id in self.nx.nodes)
|
||||||
|
|
||||||
|
return self._highest_vertex_id + 1
|
||||||
|
|
||||||
|
def _new_edge_id(self):
|
||||||
|
if self._highest_edge_id is None:
|
||||||
|
self._highest_edge_id = max(edge[EdgeConstants.I_KEY.value] for edge in self.nx.edges(keys=True))
|
||||||
|
|
||||||
|
return self._highest_edge_id + 1
|
||||||
|
|
||||||
|
def create_vertex(self) -> "Vertex":
|
||||||
|
vertex_id = self._new_vertex_id()
|
||||||
|
|
||||||
|
self.nx.add_node(vertex_id)
|
||||||
|
self._highest_vertex_id = vertex_id
|
||||||
|
|
||||||
|
return Vertex(vertex_id, self)
|
||||||
|
|
||||||
|
def create_edge(self, from_vertex: "Vertex", to_vertex: "Vertex", edge_type: str) -> "Edge":
|
||||||
|
if from_vertex.is_deleted() or to_vertex.is_deleted():
|
||||||
|
raise DeletedObjectError("Accessing deleted object.")
|
||||||
|
|
||||||
|
edge_id = self._new_edge_id()
|
||||||
|
|
||||||
|
from_id = from_vertex.id
|
||||||
|
to_id = to_vertex.id
|
||||||
|
|
||||||
|
self.nx.add_edge(from_id, to_id, key=edge_id, type=edge_type)
|
||||||
|
self._highest_edge_id = edge_id
|
||||||
|
|
||||||
|
return Edge((from_id, to_id, edge_id), self)
|
||||||
|
|
||||||
|
def delete_vertex(self, vertex_id: int):
|
||||||
|
self.nx.remove_node(vertex_id)
|
||||||
|
|
||||||
|
def delete_edge(self, from_vertex_id: int, to_vertex_id: int, edge_id: int):
|
||||||
|
self.nx.remove_edge(from_vertex_id, to_vertex_id, edge_id)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def highest_vertex_id(self) -> int:
|
||||||
|
if self._highest_vertex_id is None:
|
||||||
|
self._highest_vertex_id = max(vertex_id for vertex_id in self.nx.nodes) + 1
|
||||||
|
|
||||||
|
return self._highest_vertex_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def highest_edge_id(self) -> int:
|
||||||
|
if self._highest_edge_id is None:
|
||||||
|
self._highest_edge_id = max(edge[EdgeConstants.I_KEY.value] for edge in self.nx.edges(keys=True))
|
||||||
|
|
||||||
|
return self._highest_edge_id + 1
|
||||||
|
|
||||||
|
|
||||||
|
class Vertex:
|
||||||
|
"""Represents a graph vertex."""
|
||||||
|
|
||||||
|
__slots__ = ("_id", "_graph")
|
||||||
|
|
||||||
|
def __init__(self, id: int, graph: Graph) -> None:
|
||||||
|
if not isinstance(id, int):
|
||||||
|
raise TypeError(f"Expected 'int', got '{type(id)}'")
|
||||||
|
|
||||||
|
if not isinstance(graph, Graph):
|
||||||
|
raise TypeError(f"Expected '_mgp_mock.Graph', got '{type(graph)}'")
|
||||||
|
|
||||||
|
if not graph.nx.has_node(id):
|
||||||
|
raise IndexError(f"Unable to find vertex with ID {id}.")
|
||||||
|
|
||||||
|
self._id = id
|
||||||
|
self._graph = graph
|
||||||
|
|
||||||
|
def is_valid(self) -> bool:
|
||||||
|
return self._graph.is_valid()
|
||||||
|
|
||||||
|
def is_deleted(self) -> bool:
|
||||||
|
return not self._graph.nx.has_node(self._id) and self._id <= self._graph.highest_vertex_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def underlying_graph(self) -> Graph:
|
||||||
|
return self._graph
|
||||||
|
|
||||||
|
def underlying_graph_is_mutable(self) -> bool:
|
||||||
|
return not nx.is_frozen(self._graph.nx)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def labels(self) -> typing.List[int]:
|
||||||
|
return self._graph.nx.nodes[self._id][NX_LABEL_ATTR].split(":")
|
||||||
|
|
||||||
|
def add_label(self, label: str) -> None:
|
||||||
|
if nx.is_frozen(self._graph.nx):
|
||||||
|
raise ImmutableObjectError("Cannot modify immutable object.")
|
||||||
|
|
||||||
|
self._graph.nx.nodes[self._id][NX_LABEL_ATTR] += f":{label}"
|
||||||
|
|
||||||
|
def remove_label(self, label: str) -> None:
|
||||||
|
if nx.is_frozen(self._graph.nx):
|
||||||
|
raise ImmutableObjectError("Cannot modify immutable object.")
|
||||||
|
|
||||||
|
labels = self._graph.nx.nodes[self._id][NX_LABEL_ATTR]
|
||||||
|
if labels.startswith(f"{label}:"):
|
||||||
|
labels = "\n" + labels # pseudo-string starter
|
||||||
|
self._graph.nx.nodes[self._id][NX_LABEL_ATTR] = labels.replace(f"\n{label}:", "")
|
||||||
|
elif labels.endswith(f":{label}"):
|
||||||
|
labels += "\n" # pseudo-string terminator
|
||||||
|
self._graph.nx.nodes[self._id][NX_LABEL_ATTR] = labels.replace(f":{label}\n", "")
|
||||||
|
else:
|
||||||
|
self._graph.nx.nodes[self._id][NX_LABEL_ATTR] = labels.replace(f":{label}:", ":")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id(self) -> int:
|
||||||
|
return self._id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def properties(self):
|
||||||
|
return (
|
||||||
|
(key, value)
|
||||||
|
for key, value in self._graph.nx.nodes[self._id].items()
|
||||||
|
if key not in (NX_LABEL_ATTR, NX_TYPE_ATTR)
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_property(self, property_name: str):
|
||||||
|
return self._graph.nx.nodes[self._id][property_name]
|
||||||
|
|
||||||
|
def set_property(self, property_name: str, value: object):
|
||||||
|
self._graph.nx.nodes[self._id][property_name] = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def in_edges(self) -> typing.Iterable["Edge"]:
|
||||||
|
return [Edge(edge, self._graph) for edge in self._graph.nx.in_edges(self._id, keys=True)]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def out_edges(self) -> typing.Iterable["Edge"]:
|
||||||
|
return [Edge(edge, self._graph) for edge in self._graph.nx.out_edges(self._id, keys=True)]
|
||||||
|
|
||||||
|
|
||||||
|
class Edge:
|
||||||
|
"""Represents a graph edge."""
|
||||||
|
|
||||||
|
__slots__ = ("_edge", "_graph")
|
||||||
|
|
||||||
|
def __init__(self, edge: typing.Tuple[int, int, int], graph: Graph) -> None:
|
||||||
|
if not isinstance(edge, typing.Tuple):
|
||||||
|
raise TypeError(f"Expected 'Tuple', got '{type(edge)}'")
|
||||||
|
|
||||||
|
if not isinstance(graph, Graph):
|
||||||
|
raise TypeError(f"Expected '_mgp_mock.Graph', got '{type(graph)}'")
|
||||||
|
|
||||||
|
if not graph.nx.has_edge(*edge):
|
||||||
|
raise IndexError(f"Unable to find edge with ID {edge[EdgeConstants.I_KEY.value]}.")
|
||||||
|
|
||||||
|
self._edge = edge
|
||||||
|
self._graph = graph
|
||||||
|
|
||||||
|
def is_valid(self) -> bool:
|
||||||
|
return self._graph.is_valid()
|
||||||
|
|
||||||
|
def is_deleted(self) -> bool:
|
||||||
|
return (
|
||||||
|
not self._graph.nx.has_edge(*self._edge)
|
||||||
|
and self._edge[EdgeConstants.I_KEY.value] <= self._graph.highest_edge_id
|
||||||
|
)
|
||||||
|
|
||||||
|
def underlying_graph_is_mutable(self) -> bool:
|
||||||
|
return not nx.is_frozen(self._graph.nx)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id(self) -> int:
|
||||||
|
return self._edge[EdgeConstants.I_KEY.value]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def edge(self) -> typing.Tuple[int, int, int]:
|
||||||
|
return self._edge
|
||||||
|
|
||||||
|
@property
|
||||||
|
def start_id(self) -> int:
|
||||||
|
return self._edge[EdgeConstants.I_START.value]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def end_id(self) -> int:
|
||||||
|
return self._edge[EdgeConstants.I_END.value]
|
||||||
|
|
||||||
|
def get_type_name(self):
|
||||||
|
return self._graph.nx.get_edge_data(*self._edge)[NX_TYPE_ATTR]
|
||||||
|
|
||||||
|
def from_vertex(self) -> Vertex:
|
||||||
|
return Vertex(self.start_id, self._graph)
|
||||||
|
|
||||||
|
def to_vertex(self) -> Vertex:
|
||||||
|
return Vertex(self.end_id, self._graph)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def properties(self):
|
||||||
|
return (
|
||||||
|
(key, value)
|
||||||
|
for key, value in self._graph.nx.edges[self._edge].items()
|
||||||
|
if key not in (NX_LABEL_ATTR, NX_TYPE_ATTR)
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_property(self, property_name: str):
|
||||||
|
return self._graph.nx.edges[self._edge][property_name]
|
||||||
|
|
||||||
|
def set_property(self, property_name: str, value: object):
|
||||||
|
self._graph.nx.edges[self._edge][property_name] = value
|
||||||
|
|
||||||
|
|
||||||
|
class Path:
|
||||||
|
"""Represents a path comprised of `Vertex` and `Edge` instances."""
|
||||||
|
|
||||||
|
__slots__ = ("_vertices", "_edges", "_graph")
|
||||||
|
__create_key = object()
|
||||||
|
|
||||||
|
def __init__(self, create_key, vertex_id: int, graph: Graph) -> None:
|
||||||
|
assert create_key == Path.__create_key, "Path objects must be created using Path.make_with_start"
|
||||||
|
|
||||||
|
self._vertices = [vertex_id]
|
||||||
|
self._edges = []
|
||||||
|
self._graph = graph
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def make_with_start(cls, vertex: Vertex) -> "Path":
|
||||||
|
if not isinstance(vertex, Vertex):
|
||||||
|
raise TypeError(f"Expected 'Vertex', got '{type(vertex)}'")
|
||||||
|
|
||||||
|
if not isinstance(vertex.underlying_graph, Graph):
|
||||||
|
raise TypeError(f"Expected '_mgp_mock.Graph', got '{type(vertex.underlying_graph)}'")
|
||||||
|
|
||||||
|
if not vertex.underlying_graph.nx.has_node(vertex._id):
|
||||||
|
raise IndexError(f"Unable to find vertex with ID {vertex._id}.")
|
||||||
|
|
||||||
|
return Path(cls.__create_key, vertex._id, vertex.underlying_graph)
|
||||||
|
|
||||||
|
def is_valid(self) -> bool:
|
||||||
|
return self._graph.is_valid()
|
||||||
|
|
||||||
|
def underlying_graph_is_mutable(self) -> bool:
|
||||||
|
return not nx.is_frozen(self._graph.nx)
|
||||||
|
|
||||||
|
def expand(self, edge: Edge):
|
||||||
|
if edge.start_id != self._vertices[-1]:
|
||||||
|
raise LogicErrorError("Logic error.")
|
||||||
|
|
||||||
|
self._vertices.append(edge.end_id)
|
||||||
|
self._edges.append((edge.start_id, edge.end_id, edge.id))
|
||||||
|
|
||||||
|
def vertex_at(self, index: int) -> Vertex:
|
||||||
|
return Vertex(self._vertices[index], self._graph)
|
||||||
|
|
||||||
|
def edge_at(self, index: int) -> Edge:
|
||||||
|
return Edge(self._edges[index], self._graph)
|
||||||
|
|
||||||
|
def size(self) -> int:
|
||||||
|
return len(self._edges)
|
1655
include/mgp_mock.py
Normal file
1655
include/mgp_mock.py
Normal file
File diff suppressed because it is too large
Load Diff
@ -106,6 +106,10 @@ install(PROGRAMS $<TARGET_FILE:memgraph>
|
|||||||
# Install Python source for supporting our embedded Python.
|
# Install Python source for supporting our embedded Python.
|
||||||
install(FILES ${CMAKE_SOURCE_DIR}/include/mgp.py
|
install(FILES ${CMAKE_SOURCE_DIR}/include/mgp.py
|
||||||
DESTINATION lib/memgraph/python_support)
|
DESTINATION lib/memgraph/python_support)
|
||||||
|
install(FILES ${CMAKE_SOURCE_DIR}/include/mgp_mock.py
|
||||||
|
DESTINATION lib/memgraph/python_support)
|
||||||
|
install(FILES ${CMAKE_SOURCE_DIR}/include/_mgp_mock.py
|
||||||
|
DESTINATION lib/memgraph/python_support)
|
||||||
|
|
||||||
# Install the includes file for writing custom procedures in C and C++>
|
# Install the includes file for writing custom procedures in C and C++>
|
||||||
install(FILES ${CMAKE_SOURCE_DIR}/include/mg_procedure.h
|
install(FILES ${CMAKE_SOURCE_DIR}/include/mg_procedure.h
|
||||||
|
@ -44,6 +44,7 @@ add_subdirectory(module_file_manager)
|
|||||||
add_subdirectory(monitoring_server)
|
add_subdirectory(monitoring_server)
|
||||||
add_subdirectory(lba_procedures)
|
add_subdirectory(lba_procedures)
|
||||||
add_subdirectory(python_query_modules_reloading)
|
add_subdirectory(python_query_modules_reloading)
|
||||||
|
add_subdirectory(mock_api)
|
||||||
|
|
||||||
copy_e2e_python_files(pytest_runner pytest_runner.sh "")
|
copy_e2e_python_files(pytest_runner pytest_runner.sh "")
|
||||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/memgraph-selfsigned.crt DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/memgraph-selfsigned.crt DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
8
tests/e2e/mock_api/CMakeLists.txt
Normal file
8
tests/e2e/mock_api/CMakeLists.txt
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
function(copy_mock_python_api_e2e_files FILE_NAME)
|
||||||
|
copy_e2e_python_files(mock_python_api ${FILE_NAME})
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
add_subdirectory(procedures)
|
||||||
|
|
||||||
|
copy_mock_python_api_e2e_files(common.py)
|
||||||
|
copy_mock_python_api_e2e_files(test_compare_mock.py)
|
14
tests/e2e/mock_api/common.py
Normal file
14
tests/e2e/mock_api/common.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import typing
|
||||||
|
|
||||||
|
import mgclient
|
||||||
|
|
||||||
|
|
||||||
|
def connect(**kwargs) -> mgclient.Connection:
|
||||||
|
connection = mgclient.connect(host="localhost", port=7687, **kwargs)
|
||||||
|
connection.autocommit = True
|
||||||
|
return connection
|
||||||
|
|
||||||
|
|
||||||
|
def execute_and_fetch_results_dict(cursor, query) -> typing.Dict:
|
||||||
|
cursor.execute(query)
|
||||||
|
return cursor.fetchall()[0][0]
|
10
tests/e2e/mock_api/procedures/CMakeLists.txt
Normal file
10
tests/e2e/mock_api/procedures/CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
copy_mock_python_api_e2e_files(test_utils.py)
|
||||||
|
copy_mock_python_api_e2e_files(edge_type.py)
|
||||||
|
copy_mock_python_api_e2e_files(edge.py)
|
||||||
|
copy_mock_python_api_e2e_files(graph.py)
|
||||||
|
copy_mock_python_api_e2e_files(label.py)
|
||||||
|
copy_mock_python_api_e2e_files(path.py)
|
||||||
|
copy_mock_python_api_e2e_files(properties.py)
|
||||||
|
copy_mock_python_api_e2e_files(record.py)
|
||||||
|
copy_mock_python_api_e2e_files(vertex.py)
|
||||||
|
copy_mock_python_api_e2e_files(vertices.py)
|
85
tests/e2e/mock_api/procedures/edge.py
Normal file
85
tests/e2e/mock_api/procedures/edge.py
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
import mgp
|
||||||
|
import mgp_mock
|
||||||
|
import test_utils
|
||||||
|
|
||||||
|
|
||||||
|
@mgp.read_proc
|
||||||
|
def compare_apis(ctx: mgp.ProcCtx) -> mgp.Record(results_dict=mgp.Map):
|
||||||
|
mock_ctx = test_utils.get_mock_proc_ctx(is_write=False)
|
||||||
|
results = dict()
|
||||||
|
|
||||||
|
TARGET_EDGE_1_ID = 9
|
||||||
|
TARGET_EDGE_2_ID = 37
|
||||||
|
|
||||||
|
target_edge_1 = test_utils.get_edge(ctx, permanent_id=TARGET_EDGE_1_ID)
|
||||||
|
target_edge_2 = test_utils.get_edge(ctx, permanent_id=TARGET_EDGE_2_ID)
|
||||||
|
target_mock_edge_1 = test_utils.get_mock_edge(mock_ctx, id=TARGET_EDGE_1_ID)
|
||||||
|
target_mock_edge_2 = test_utils.get_mock_edge(mock_ctx, id=TARGET_EDGE_2_ID)
|
||||||
|
|
||||||
|
results["is_valid"] = test_utils.all_equal(
|
||||||
|
target_edge_1.is_valid(),
|
||||||
|
target_mock_edge_1.is_valid(),
|
||||||
|
True,
|
||||||
|
)
|
||||||
|
|
||||||
|
results["underlying_graph_is_mutable"] = test_utils.all_equal(
|
||||||
|
target_edge_1.underlying_graph_is_mutable(),
|
||||||
|
target_mock_edge_1.underlying_graph_is_mutable(),
|
||||||
|
False,
|
||||||
|
)
|
||||||
|
|
||||||
|
results["id"] = test_utils.all_equal(
|
||||||
|
isinstance(target_edge_1.id, int),
|
||||||
|
isinstance(target_mock_edge_1.id, int),
|
||||||
|
True,
|
||||||
|
)
|
||||||
|
|
||||||
|
results["type"] = test_utils.all_equal(
|
||||||
|
target_edge_1.type.name,
|
||||||
|
target_mock_edge_1.type.name,
|
||||||
|
"HAS_TEAM",
|
||||||
|
)
|
||||||
|
|
||||||
|
results["from_vertex"] = test_utils.all_equal(
|
||||||
|
isinstance(target_edge_1.from_vertex, mgp.Vertex),
|
||||||
|
isinstance(target_mock_edge_1.from_vertex, mgp_mock.Vertex),
|
||||||
|
True,
|
||||||
|
)
|
||||||
|
|
||||||
|
results["to_vertex"] = test_utils.all_equal(
|
||||||
|
isinstance(target_edge_1.to_vertex, mgp.Vertex),
|
||||||
|
isinstance(target_mock_edge_1.to_vertex, mgp_mock.Vertex),
|
||||||
|
True,
|
||||||
|
)
|
||||||
|
|
||||||
|
results["properties"] = test_utils.all_equal(
|
||||||
|
isinstance(target_edge_1.properties, mgp.Properties),
|
||||||
|
isinstance(target_mock_edge_1.properties, mgp_mock.Properties),
|
||||||
|
True,
|
||||||
|
) and test_utils.all_equal(
|
||||||
|
{prop.name: prop.value for prop in target_edge_1.properties.items()},
|
||||||
|
{prop.name: prop.value for prop in target_mock_edge_1.properties.items()},
|
||||||
|
{"permanent_id": 9},
|
||||||
|
)
|
||||||
|
|
||||||
|
results["__eq__"] = test_utils.all_equal(
|
||||||
|
target_edge_1 == target_edge_1,
|
||||||
|
target_mock_edge_1 == target_mock_edge_1,
|
||||||
|
True,
|
||||||
|
) and test_utils.all_equal(
|
||||||
|
target_edge_1 != target_edge_1,
|
||||||
|
target_mock_edge_1 != target_mock_edge_1,
|
||||||
|
False,
|
||||||
|
)
|
||||||
|
|
||||||
|
results["__ne__"] = test_utils.all_equal(
|
||||||
|
target_edge_1 != target_edge_2,
|
||||||
|
target_mock_edge_1 != target_mock_edge_2,
|
||||||
|
True,
|
||||||
|
) and test_utils.all_equal(
|
||||||
|
target_edge_1 == target_edge_2,
|
||||||
|
target_mock_edge_1 == target_mock_edge_2,
|
||||||
|
False,
|
||||||
|
)
|
||||||
|
|
||||||
|
return mgp.Record(results_dict=results)
|
33
tests/e2e/mock_api/procedures/edge_type.py
Normal file
33
tests/e2e/mock_api/procedures/edge_type.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import mgp
|
||||||
|
import test_utils
|
||||||
|
|
||||||
|
|
||||||
|
@mgp.read_proc
|
||||||
|
def compare_apis(ctx: mgp.ProcCtx) -> mgp.Record(results_dict=mgp.Map):
|
||||||
|
mock_ctx = test_utils.get_mock_proc_ctx(is_write=False)
|
||||||
|
results = dict()
|
||||||
|
|
||||||
|
TARGET_EDGE_ID = 0
|
||||||
|
|
||||||
|
target_edge_type = test_utils.get_edge(ctx, permanent_id=TARGET_EDGE_ID).type
|
||||||
|
target_mock_edge_type = test_utils.get_mock_edge(mock_ctx, id=TARGET_EDGE_ID).type
|
||||||
|
|
||||||
|
results["name"] = test_utils.all_equal(
|
||||||
|
target_edge_type.name,
|
||||||
|
target_mock_edge_type.name,
|
||||||
|
"IS_PART_OF",
|
||||||
|
)
|
||||||
|
|
||||||
|
results["__eq__"] = test_utils.all_equal(
|
||||||
|
target_edge_type == target_edge_type,
|
||||||
|
target_edge_type == "IS_PART_OF",
|
||||||
|
target_mock_edge_type == target_mock_edge_type,
|
||||||
|
target_mock_edge_type == "IS_PART_OF",
|
||||||
|
)
|
||||||
|
|
||||||
|
results["__ne__"] = test_utils.all_equal(
|
||||||
|
target_edge_type != "HAS_TEAM",
|
||||||
|
target_mock_edge_type != "HAS_TEAM",
|
||||||
|
)
|
||||||
|
|
||||||
|
return mgp.Record(results_dict=results)
|
102
tests/e2e/mock_api/procedures/graph.py
Normal file
102
tests/e2e/mock_api/procedures/graph.py
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
import mgp
|
||||||
|
import mgp_mock
|
||||||
|
import test_utils
|
||||||
|
|
||||||
|
|
||||||
|
@mgp.write_proc
|
||||||
|
def compare_apis(ctx: mgp.ProcCtx) -> mgp.Record(results_dict=mgp.Map):
|
||||||
|
VERTEX_ID = 6
|
||||||
|
|
||||||
|
mock_ctx = test_utils.get_mock_proc_ctx(is_write=True)
|
||||||
|
results = dict()
|
||||||
|
|
||||||
|
results["is_valid"] = test_utils.all_equal(
|
||||||
|
ctx.graph.is_valid(),
|
||||||
|
mock_ctx.graph.is_valid(),
|
||||||
|
True,
|
||||||
|
)
|
||||||
|
|
||||||
|
results["get_vertex_by_id"] = test_utils.all_equal(
|
||||||
|
test_utils.get_vertex(ctx, permanent_id=VERTEX_ID).properties["permanent_id"],
|
||||||
|
mock_ctx.graph.get_vertex_by_id(VERTEX_ID).properties["permanent_id"],
|
||||||
|
VERTEX_ID,
|
||||||
|
)
|
||||||
|
|
||||||
|
results["vertices"] = test_utils.all_equal(
|
||||||
|
len(ctx.graph.vertices),
|
||||||
|
len(mock_ctx.graph.vertices),
|
||||||
|
27,
|
||||||
|
)
|
||||||
|
|
||||||
|
results["is_mutable"] = test_utils.all_equal(
|
||||||
|
ctx.graph.is_mutable(),
|
||||||
|
mock_ctx.graph.is_mutable(),
|
||||||
|
True,
|
||||||
|
)
|
||||||
|
|
||||||
|
new_mock_vertex = mock_ctx.graph.create_vertex()
|
||||||
|
new_mock_vertex_id = new_mock_vertex.id
|
||||||
|
results["create_vertex"] = test_utils.all_equal(
|
||||||
|
new_mock_vertex_id in [v.id for v in mock_ctx.graph.vertices],
|
||||||
|
True,
|
||||||
|
)
|
||||||
|
|
||||||
|
mock_ctx.graph.delete_vertex(new_mock_vertex)
|
||||||
|
results["delete_vertex"] = test_utils.all_equal(
|
||||||
|
new_mock_vertex_id not in [v.id for v in mock_ctx.graph.vertices],
|
||||||
|
True,
|
||||||
|
)
|
||||||
|
|
||||||
|
mock_vertex_to_delete = mock_ctx.graph.get_vertex_by_id(VERTEX_ID)
|
||||||
|
mock_ctx.graph.detach_delete_vertex(mock_vertex_to_delete)
|
||||||
|
results["detach_delete_vertex"] = test_utils.all_equal(
|
||||||
|
VERTEX_ID not in [v.properties["permanent_id"] for v in mock_ctx.graph.vertices],
|
||||||
|
True,
|
||||||
|
)
|
||||||
|
|
||||||
|
MAX_EDGE_ID = 37
|
||||||
|
|
||||||
|
START_ID = 10
|
||||||
|
END1_ID = 13
|
||||||
|
END2_ID = 14
|
||||||
|
start_mock_vertex, end1_mock_vertex, end2_mock_vertex = (
|
||||||
|
mock_ctx.graph.get_vertex_by_id(START_ID),
|
||||||
|
mock_ctx.graph.get_vertex_by_id(END1_ID),
|
||||||
|
mock_ctx.graph.get_vertex_by_id(END2_ID),
|
||||||
|
)
|
||||||
|
EDGE_TYPE = "CONNECTED_TO"
|
||||||
|
mock_edge_type = mgp_mock.EdgeType(EDGE_TYPE)
|
||||||
|
new_mock_edge = mock_ctx.graph.create_edge(start_mock_vertex, end1_mock_vertex, mock_edge_type)
|
||||||
|
new_mock_edge_id = new_mock_edge.id
|
||||||
|
results["create_edge"] = test_utils.all_equal(
|
||||||
|
new_mock_edge_id,
|
||||||
|
MAX_EDGE_ID + 1,
|
||||||
|
)
|
||||||
|
|
||||||
|
mock_ctx.graph.delete_edge(new_mock_edge)
|
||||||
|
results["delete_edge"] = test_utils.all_equal(
|
||||||
|
new_mock_edge_id not in [e.id for e in start_mock_vertex.out_edges],
|
||||||
|
True,
|
||||||
|
)
|
||||||
|
|
||||||
|
another_mock_edge = mock_ctx.graph.create_edge(start_mock_vertex, end2_mock_vertex, mock_edge_type)
|
||||||
|
results["edge_id_assignment"] = test_utils.all_equal(
|
||||||
|
another_mock_edge.id,
|
||||||
|
MAX_EDGE_ID + 2,
|
||||||
|
)
|
||||||
|
|
||||||
|
return mgp.Record(results_dict=results)
|
||||||
|
|
||||||
|
|
||||||
|
@mgp.read_proc
|
||||||
|
def test_read_proc_mutability(ctx: mgp.ProcCtx) -> mgp.Record(results_dict=mgp.Map):
|
||||||
|
mock_ctx = test_utils.get_mock_proc_ctx(is_write=False)
|
||||||
|
results = dict()
|
||||||
|
|
||||||
|
results["is_not_mutable"] = test_utils.all_equal(
|
||||||
|
ctx.graph.is_mutable(),
|
||||||
|
mock_ctx.graph.is_mutable(),
|
||||||
|
False,
|
||||||
|
)
|
||||||
|
|
||||||
|
return mgp.Record(results_dict=results)
|
52
tests/e2e/mock_api/procedures/label.py
Normal file
52
tests/e2e/mock_api/procedures/label.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import mgp
|
||||||
|
import test_utils
|
||||||
|
|
||||||
|
|
||||||
|
@mgp.read_proc
|
||||||
|
def compare_apis(ctx: mgp.ProcCtx) -> mgp.Record(results_dict=mgp.Map):
|
||||||
|
mock_ctx = test_utils.get_mock_proc_ctx(is_write=False)
|
||||||
|
results = dict()
|
||||||
|
|
||||||
|
TARGET_LABELLED_NODE_ID = 5
|
||||||
|
|
||||||
|
target_vertex = test_utils.get_vertex(ctx, permanent_id=TARGET_LABELLED_NODE_ID)
|
||||||
|
target_mock_vertex = mock_ctx.graph.get_vertex_by_id(TARGET_LABELLED_NODE_ID)
|
||||||
|
|
||||||
|
label_1, label_2 = sorted(target_vertex.labels, key=lambda l: l.name) # ("Company", "Startup")
|
||||||
|
mock_label_1, mock_label_2 = sorted(target_mock_vertex.labels, key=lambda l: l.name) # ditto
|
||||||
|
|
||||||
|
results["name"] = test_utils.all_equal(
|
||||||
|
(label_1.name, label_2.name),
|
||||||
|
(mock_label_1.name, mock_label_2.name),
|
||||||
|
("Company", "Startup"),
|
||||||
|
)
|
||||||
|
|
||||||
|
results["__eq__"] = test_utils.all_equal(
|
||||||
|
label_1 == label_1,
|
||||||
|
label_1 == "Company",
|
||||||
|
mock_label_1 == mock_label_1,
|
||||||
|
mock_label_1 == "Company",
|
||||||
|
True,
|
||||||
|
) and test_utils.all_equal(
|
||||||
|
label_1 == label_2,
|
||||||
|
label_1 == "Startup",
|
||||||
|
mock_label_1 == mock_label_2,
|
||||||
|
mock_label_1 == "Startup",
|
||||||
|
False,
|
||||||
|
)
|
||||||
|
|
||||||
|
results["__ne__"] = test_utils.all_equal(
|
||||||
|
label_1 != label_2,
|
||||||
|
label_1 != "Startup",
|
||||||
|
mock_label_1 != mock_label_2,
|
||||||
|
mock_label_1 != "Startup",
|
||||||
|
True,
|
||||||
|
) and test_utils.all_equal(
|
||||||
|
label_1 != label_1,
|
||||||
|
label_1 != "Company",
|
||||||
|
mock_label_1 != mock_label_1,
|
||||||
|
mock_label_1 != "Company",
|
||||||
|
False,
|
||||||
|
)
|
||||||
|
|
||||||
|
return mgp.Record(results_dict=results)
|
65
tests/e2e/mock_api/procedures/path.py
Normal file
65
tests/e2e/mock_api/procedures/path.py
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import copy
|
||||||
|
|
||||||
|
import mgp
|
||||||
|
import mgp_mock
|
||||||
|
import test_utils
|
||||||
|
|
||||||
|
|
||||||
|
@mgp.read_proc
|
||||||
|
def compare_apis(ctx: mgp.ProcCtx) -> mgp.Record(results_dict=mgp.Map):
|
||||||
|
mock_ctx = test_utils.get_mock_proc_ctx(is_write=False)
|
||||||
|
results = dict()
|
||||||
|
|
||||||
|
START_ID = 0
|
||||||
|
start_vertex = test_utils.get_vertex(ctx, permanent_id=START_ID)
|
||||||
|
mock_start_vertex = mock_ctx.graph.get_vertex_by_id(START_ID)
|
||||||
|
path = mgp.Path(start_vertex)
|
||||||
|
mock_path = mgp_mock.Path(mock_start_vertex)
|
||||||
|
|
||||||
|
results["is_valid"] = test_utils.all_equal(
|
||||||
|
path.is_valid(),
|
||||||
|
mock_path.is_valid(),
|
||||||
|
True,
|
||||||
|
)
|
||||||
|
|
||||||
|
EDGE_ID = 0
|
||||||
|
edge_to_add = test_utils.get_edge(ctx, permanent_id=EDGE_ID)
|
||||||
|
mock_edge_to_add = test_utils.get_mock_edge(mock_ctx, id=EDGE_ID)
|
||||||
|
path.expand(edge_to_add)
|
||||||
|
mock_path.expand(mock_edge_to_add)
|
||||||
|
results["expand"] = test_utils.all_equal(
|
||||||
|
(len(path.vertices), len(path.edges)),
|
||||||
|
(len(mock_path.vertices), len(mock_path.edges)),
|
||||||
|
(2, 1),
|
||||||
|
)
|
||||||
|
|
||||||
|
NEXT_ID = 1
|
||||||
|
results["vertices"] = test_utils.all_equal(
|
||||||
|
all(isinstance(vertex, mgp.Vertex) for vertex in path.vertices),
|
||||||
|
all(isinstance(vertex, mgp_mock.Vertex) for vertex in mock_path.vertices),
|
||||||
|
True,
|
||||||
|
) and test_utils.all_equal(
|
||||||
|
[vertex.properties["permanent_id"] for vertex in path.vertices],
|
||||||
|
[vertex.properties["permanent_id"] for vertex in mock_path.vertices],
|
||||||
|
[START_ID, NEXT_ID],
|
||||||
|
)
|
||||||
|
|
||||||
|
results["edges"] = test_utils.all_equal(
|
||||||
|
all(isinstance(edge, mgp.Edge) for edge in path.edges),
|
||||||
|
all(isinstance(edge, mgp_mock.Edge) for edge in mock_path.edges),
|
||||||
|
True,
|
||||||
|
) and test_utils.all_equal(
|
||||||
|
[edge.properties["permanent_id"] for edge in path.edges],
|
||||||
|
[edge.properties["permanent_id"] for edge in mock_path.edges],
|
||||||
|
[0],
|
||||||
|
)
|
||||||
|
|
||||||
|
path_copy = copy.copy(path)
|
||||||
|
mock_path_copy = copy.copy(mock_path)
|
||||||
|
results["__copy__"] = test_utils.all_equal(
|
||||||
|
path_copy.is_valid(),
|
||||||
|
mock_path_copy.is_valid(),
|
||||||
|
True,
|
||||||
|
)
|
||||||
|
|
||||||
|
return mgp.Record(results_dict=results)
|
184
tests/e2e/mock_api/procedures/properties.py
Normal file
184
tests/e2e/mock_api/procedures/properties.py
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
import mgp
|
||||||
|
import test_utils
|
||||||
|
|
||||||
|
|
||||||
|
@mgp.write_proc
|
||||||
|
def compare_apis_on_vertex(ctx: mgp.ProcCtx) -> mgp.Record(results_dict=mgp.Map):
|
||||||
|
mock_ctx = test_utils.get_mock_proc_ctx(is_write=True)
|
||||||
|
results = dict()
|
||||||
|
|
||||||
|
TARGET_ID = 0
|
||||||
|
target_vertex = test_utils.get_vertex(ctx, permanent_id=TARGET_ID)
|
||||||
|
target_mock_vertex = mock_ctx.graph.get_vertex_by_id(TARGET_ID)
|
||||||
|
|
||||||
|
properties = target_vertex.properties
|
||||||
|
mock_properties = target_mock_vertex.properties
|
||||||
|
|
||||||
|
results["get"] = test_utils.all_equal(
|
||||||
|
properties.get("name"),
|
||||||
|
mock_properties.get("name"),
|
||||||
|
"Peter",
|
||||||
|
)
|
||||||
|
results["get[default]"] = test_utils.all_equal(
|
||||||
|
properties.get("YoE", default="N/A"),
|
||||||
|
mock_properties.get("YoE", default="N/A"),
|
||||||
|
"N/A",
|
||||||
|
)
|
||||||
|
|
||||||
|
properties.set("education", "PhD")
|
||||||
|
mock_properties.set("education", "PhD")
|
||||||
|
results["set"] = test_utils.all_equal(
|
||||||
|
properties.get("education"),
|
||||||
|
mock_properties.get("education"),
|
||||||
|
"PhD",
|
||||||
|
)
|
||||||
|
|
||||||
|
results["items"] = test_utils.all_equal(
|
||||||
|
{prop.name: prop.value for prop in properties.items()},
|
||||||
|
{prop.name: prop.value for prop in mock_properties.items()},
|
||||||
|
{"name": "Peter", "surname": "Yang", "education": "PhD", "permanent_id": 0},
|
||||||
|
)
|
||||||
|
|
||||||
|
results["keys"] = test_utils.all_equal(
|
||||||
|
{key for key in properties.keys()},
|
||||||
|
{key for key in mock_properties.keys()},
|
||||||
|
{"name", "surname", "education", "permanent_id"},
|
||||||
|
)
|
||||||
|
|
||||||
|
results["values"] = test_utils.all_equal(
|
||||||
|
{val for val in properties.values()},
|
||||||
|
{val for val in mock_properties.values()},
|
||||||
|
{"Peter", "Yang", "PhD", 0},
|
||||||
|
)
|
||||||
|
|
||||||
|
results["__len__"] = test_utils.all_equal(
|
||||||
|
len(properties),
|
||||||
|
len(mock_properties),
|
||||||
|
4,
|
||||||
|
)
|
||||||
|
|
||||||
|
results["__iter__"] = test_utils.all_equal(
|
||||||
|
{name for name in properties},
|
||||||
|
{name for name in mock_properties},
|
||||||
|
{"name", "surname", "education", "permanent_id"},
|
||||||
|
)
|
||||||
|
|
||||||
|
results["__getitem__"] = test_utils.all_equal(
|
||||||
|
{properties[name] for name in properties},
|
||||||
|
{mock_properties[name] for name in mock_properties},
|
||||||
|
{"Peter", "Yang", "PhD", 0},
|
||||||
|
)
|
||||||
|
|
||||||
|
properties["YoE"] = 6
|
||||||
|
mock_properties["YoE"] = 6
|
||||||
|
results["__setitem__"] = test_utils.all_equal(
|
||||||
|
properties["YoE"],
|
||||||
|
mock_properties["YoE"],
|
||||||
|
6,
|
||||||
|
)
|
||||||
|
|
||||||
|
results["__contains__"] = test_utils.all_equal(
|
||||||
|
"YoE" in properties,
|
||||||
|
"age" not in properties,
|
||||||
|
"YoE" in mock_properties,
|
||||||
|
"age" not in mock_properties,
|
||||||
|
True,
|
||||||
|
) and test_utils.all_equal(
|
||||||
|
"YoE" not in properties,
|
||||||
|
"age" in properties,
|
||||||
|
"YoE" not in mock_properties,
|
||||||
|
"age" in mock_properties,
|
||||||
|
False,
|
||||||
|
)
|
||||||
|
|
||||||
|
return mgp.Record(results_dict=results)
|
||||||
|
|
||||||
|
|
||||||
|
@mgp.write_proc
|
||||||
|
def compare_apis_on_edge(ctx: mgp.ProcCtx) -> mgp.Record(results_dict=mgp.Map):
|
||||||
|
mock_ctx = test_utils.get_mock_proc_ctx(is_write=True)
|
||||||
|
results = dict()
|
||||||
|
|
||||||
|
TARGET_EDGE_ID = 37
|
||||||
|
|
||||||
|
target_edge_properties = test_utils.get_edge(ctx, permanent_id=TARGET_EDGE_ID).properties
|
||||||
|
target_mock_edge_properties = test_utils.get_mock_edge(mock_ctx, id=TARGET_EDGE_ID).properties
|
||||||
|
|
||||||
|
results["get"] = test_utils.all_equal(
|
||||||
|
target_edge_properties.get("importance"),
|
||||||
|
target_mock_edge_properties.get("importance"),
|
||||||
|
"HIGH",
|
||||||
|
)
|
||||||
|
results["get[default]"] = test_utils.all_equal(
|
||||||
|
target_edge_properties.get("priority", default="N/A"),
|
||||||
|
target_mock_edge_properties.get("priority", default="N/A"),
|
||||||
|
"N/A",
|
||||||
|
)
|
||||||
|
|
||||||
|
target_edge_properties.set("priority", "MEDIUM")
|
||||||
|
target_mock_edge_properties.set("priority", "MEDIUM")
|
||||||
|
results["set"] = test_utils.all_equal(
|
||||||
|
target_edge_properties.get("priority"),
|
||||||
|
target_mock_edge_properties.get("priority"),
|
||||||
|
"MEDIUM",
|
||||||
|
)
|
||||||
|
|
||||||
|
results["items"] = test_utils.all_equal(
|
||||||
|
{prop.name: prop.value for prop in target_edge_properties.items()},
|
||||||
|
{prop.name: prop.value for prop in target_mock_edge_properties.items()},
|
||||||
|
{"importance": "HIGH", "priority": "MEDIUM", "permanent_id": 37},
|
||||||
|
)
|
||||||
|
|
||||||
|
results["keys"] = test_utils.all_equal(
|
||||||
|
{key for key in target_edge_properties.keys()},
|
||||||
|
{key for key in target_mock_edge_properties.keys()},
|
||||||
|
{"importance", "priority", "permanent_id"},
|
||||||
|
)
|
||||||
|
|
||||||
|
results["values"] = test_utils.all_equal(
|
||||||
|
{val for val in target_edge_properties.values()},
|
||||||
|
{val for val in target_mock_edge_properties.values()},
|
||||||
|
{"HIGH", "MEDIUM", 37},
|
||||||
|
)
|
||||||
|
|
||||||
|
results["__len__"] = test_utils.all_equal(
|
||||||
|
len(target_edge_properties),
|
||||||
|
len(target_mock_edge_properties),
|
||||||
|
3,
|
||||||
|
)
|
||||||
|
|
||||||
|
results["__iter__"] = test_utils.all_equal(
|
||||||
|
{name for name in target_edge_properties},
|
||||||
|
{name for name in target_mock_edge_properties},
|
||||||
|
{"importance", "priority", "permanent_id"},
|
||||||
|
)
|
||||||
|
|
||||||
|
results["__getitem__"] = test_utils.all_equal(
|
||||||
|
{target_edge_properties[name] for name in target_edge_properties},
|
||||||
|
{target_mock_edge_properties[name] for name in target_mock_edge_properties},
|
||||||
|
{"HIGH", "MEDIUM", 37},
|
||||||
|
)
|
||||||
|
|
||||||
|
target_edge_properties["priority"] = "LOW"
|
||||||
|
target_mock_edge_properties["priority"] = "LOW"
|
||||||
|
results["__setitem__"] = test_utils.all_equal(
|
||||||
|
target_edge_properties["priority"],
|
||||||
|
target_mock_edge_properties["priority"],
|
||||||
|
"LOW",
|
||||||
|
)
|
||||||
|
|
||||||
|
results["__contains__"] = test_utils.all_equal(
|
||||||
|
"priority" in target_edge_properties,
|
||||||
|
"status" not in target_edge_properties,
|
||||||
|
"priority" in target_mock_edge_properties,
|
||||||
|
"status" not in target_mock_edge_properties,
|
||||||
|
True,
|
||||||
|
) and test_utils.all_equal(
|
||||||
|
"priority" not in target_edge_properties,
|
||||||
|
"status" in target_edge_properties,
|
||||||
|
"priority" not in target_mock_edge_properties,
|
||||||
|
"status" in target_mock_edge_properties,
|
||||||
|
False,
|
||||||
|
)
|
||||||
|
|
||||||
|
return mgp.Record(results_dict=results)
|
18
tests/e2e/mock_api/procedures/record.py
Normal file
18
tests/e2e/mock_api/procedures/record.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import mgp
|
||||||
|
import mgp_mock
|
||||||
|
import test_utils
|
||||||
|
|
||||||
|
|
||||||
|
@mgp.read_proc
|
||||||
|
def compare_apis(ctx: mgp.ProcCtx) -> mgp.Record(results_dict=mgp.Map):
|
||||||
|
results = dict()
|
||||||
|
|
||||||
|
record = mgp.Record(a=1, b=2.0, c="3")
|
||||||
|
mock_record = mgp_mock.Record(a=1, b=2.0, c="3")
|
||||||
|
|
||||||
|
results["fields"] = test_utils.all_equal(
|
||||||
|
record.fields,
|
||||||
|
mock_record.fields,
|
||||||
|
)
|
||||||
|
|
||||||
|
return mgp.Record(results_dict=results)
|
160
tests/e2e/mock_api/procedures/test_utils.py
Normal file
160
tests/e2e/mock_api/procedures/test_utils.py
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
from itertools import groupby
|
||||||
|
|
||||||
|
import _mgp_mock
|
||||||
|
import mgp
|
||||||
|
import mgp_mock
|
||||||
|
import networkx as nx
|
||||||
|
|
||||||
|
|
||||||
|
def all_equal(*args):
|
||||||
|
"""Returns True if all the elements are equal to each other
|
||||||
|
(source: https://docs.python.org/3/library/itertools.html#itertools-recipes)"""
|
||||||
|
g = groupby(args)
|
||||||
|
return next(g, True) and not next(g, False)
|
||||||
|
|
||||||
|
|
||||||
|
def get_mock_proc_ctx(is_write: bool) -> mgp_mock.ProcCtx:
|
||||||
|
GRAPH_DATA = [
|
||||||
|
(0, 1, 0),
|
||||||
|
(5, 1, 9),
|
||||||
|
(5, 1, 37),
|
||||||
|
(10, 1, 15),
|
||||||
|
(1, 2, 4),
|
||||||
|
(1, 3, 5),
|
||||||
|
(1, 4, 6),
|
||||||
|
(0, 5, 1),
|
||||||
|
(10, 5, 16),
|
||||||
|
(22, 5, 33),
|
||||||
|
(1, 6, 7),
|
||||||
|
(6, 7, 12),
|
||||||
|
(11, 7, 18),
|
||||||
|
(13, 7, 26),
|
||||||
|
(26, 7, 35),
|
||||||
|
(6, 8, 13),
|
||||||
|
(26, 8, 36),
|
||||||
|
(0, 9, 2),
|
||||||
|
(10, 9, 17),
|
||||||
|
(22, 9, 34),
|
||||||
|
(1, 11, 8),
|
||||||
|
(9, 12, 14),
|
||||||
|
(14, 13, 27),
|
||||||
|
(0, 14, 3),
|
||||||
|
(5, 14, 11),
|
||||||
|
(12, 14, 22),
|
||||||
|
(13, 15, 23),
|
||||||
|
(13, 16, 24),
|
||||||
|
(13, 17, 25),
|
||||||
|
(11, 18, 19),
|
||||||
|
(11, 19, 20),
|
||||||
|
(11, 20, 21),
|
||||||
|
(5, 21, 10),
|
||||||
|
(22, 21, 32),
|
||||||
|
(21, 23, 28),
|
||||||
|
(21, 24, 29),
|
||||||
|
(21, 25, 30),
|
||||||
|
(21, 26, 31),
|
||||||
|
]
|
||||||
|
NODE_INFO = {
|
||||||
|
0: {"labels": "Person", "name": "Peter", "surname": "Yang", "permanent_id": 0},
|
||||||
|
1: {"labels": "Team", "name": "Engineering", "permanent_id": 1},
|
||||||
|
2: {"labels": "Repository", "name": "Memgraph", "permanent_id": 2},
|
||||||
|
3: {"labels": "Repository", "name": "MAGE", "permanent_id": 3},
|
||||||
|
4: {"labels": "Repository", "name": "GQLAlchemy", "permanent_id": 4},
|
||||||
|
5: {"labels": "Company:Startup", "name": "Memgraph", "permanent_id": 5},
|
||||||
|
6: {"labels": "File", "name": "welcome_to_engineering.txt", "permanent_id": 6},
|
||||||
|
7: {"labels": "Storage", "name": "Google Drive", "permanent_id": 7},
|
||||||
|
8: {"labels": "Storage", "name": "Notion", "permanent_id": 8},
|
||||||
|
9: {"labels": "File", "name": "welcome_to_memgraph.txt", "permanent_id": 9},
|
||||||
|
10: {"labels": "Person", "name": "Carl", "permanent_id": 10},
|
||||||
|
11: {"labels": "Folder", "name": "engineering_folder", "permanent_id": 11},
|
||||||
|
12: {"labels": "Person", "name": "Anna", "permanent_id": 12},
|
||||||
|
13: {"labels": "Folder", "name": "operations_folder", "permanent_id": 13},
|
||||||
|
14: {"labels": "Team", "name": "Operations", "permanent_id": 14},
|
||||||
|
15: {"labels": "File", "name": "operations101.txt", "permanent_id": 15},
|
||||||
|
16: {"labels": "File", "name": "expenses2022.csv", "permanent_id": 16},
|
||||||
|
17: {"labels": "File", "name": "salaries2022.csv", "permanent_id": 17},
|
||||||
|
18: {"labels": "File", "name": "engineering101.txt", "permanent_id": 18},
|
||||||
|
19: {"labels": "File", "name": "working_with_github.txt", "permanent_id": 19},
|
||||||
|
20: {"labels": "File", "name": "working_with_notion.txt", "permanent_id": 20},
|
||||||
|
21: {"labels": "Team", "name": "Marketing", "permanent_id": 21},
|
||||||
|
22: {"labels": "Person", "name": "Julie", "permanent_id": 22},
|
||||||
|
23: {"labels": "Account", "name": "Facebook", "permanent_id": 23},
|
||||||
|
24: {"labels": "Account", "name": "LinkedIn", "permanent_id": 24},
|
||||||
|
25: {"labels": "Account", "name": "HackerNews", "permanent_id": 25},
|
||||||
|
26: {"labels": "File", "name": "welcome_to_marketing.txt", "permanent_id": 26},
|
||||||
|
}
|
||||||
|
EDGE_INFO = {
|
||||||
|
(0, 1, 0): {"type": "IS_PART_OF", "permanent_id": 0},
|
||||||
|
(0, 5, 1): {"type": "IS_PART_OF", "permanent_id": 1},
|
||||||
|
(0, 9, 2): {"type": "HAS_ACCESS_TO", "permanent_id": 2},
|
||||||
|
(0, 14, 3): {"type": "IS_PART_OF", "permanent_id": 3},
|
||||||
|
(1, 2, 4): {"type": "HAS_ACCESS_TO", "permanent_id": 4},
|
||||||
|
(1, 3, 5): {"type": "HAS_ACCESS_TO", "permanent_id": 5},
|
||||||
|
(1, 4, 6): {"type": "HAS_ACCESS_TO", "permanent_id": 6},
|
||||||
|
(1, 6, 7): {"type": "HAS_ACCESS_TO", "permanent_id": 7},
|
||||||
|
(1, 11, 8): {"type": "HAS_ACCESS_TO", "permanent_id": 8},
|
||||||
|
(5, 1, 9): {"type": "HAS_TEAM", "permanent_id": 9},
|
||||||
|
(5, 1, 37): {"type": "HAS_TEAM_2", "importance": "HIGH", "permanent_id": 37},
|
||||||
|
(5, 14, 11): {"type": "HAS_TEAM", "permanent_id": 11},
|
||||||
|
(5, 21, 10): {"type": "HAS_TEAM", "permanent_id": 10},
|
||||||
|
(6, 7, 12): {"type": "IS_STORED_IN", "permanent_id": 12},
|
||||||
|
(6, 8, 13): {"type": "IS_STORED_IN", "permanent_id": 13},
|
||||||
|
(9, 12, 14): {"type": "CREATED_BY", "permanent_id": 14},
|
||||||
|
(10, 1, 15): {"type": "IS_PART_OF", "permanent_id": 15},
|
||||||
|
(10, 5, 16): {"type": "IS_PART_OF", "permanent_id": 16},
|
||||||
|
(10, 9, 17): {"type": "HAS_ACCESS_TO", "permanent_id": 17},
|
||||||
|
(11, 7, 18): {"type": "IS_STORED_IN", "permanent_id": 18},
|
||||||
|
(11, 18, 19): {"type": "HAS_ACCESS_TO", "permanent_id": 19},
|
||||||
|
(11, 19, 20): {"type": "HAS_ACCESS_TO", "permanent_id": 20},
|
||||||
|
(11, 20, 21): {"type": "HAS_ACCESS_TO", "permanent_id": 21},
|
||||||
|
(12, 14, 22): {"type": "IS_PART_OF", "permanent_id": 22},
|
||||||
|
(13, 7, 26): {"type": "IS_STORED_IN", "permanent_id": 26},
|
||||||
|
(13, 15, 23): {"type": "HAS_ACCESS_TO", "permanent_id": 23},
|
||||||
|
(13, 16, 24): {"type": "HAS_ACCESS_TO", "permanent_id": 24},
|
||||||
|
(13, 17, 25): {"type": "HAS_ACCESS_TO", "permanent_id": 25},
|
||||||
|
(14, 13, 27): {"type": "HAS_ACCESS_TO", "permanent_id": 27},
|
||||||
|
(21, 23, 28): {"type": "HAS_ACCESS_TO", "permanent_id": 28},
|
||||||
|
(21, 24, 29): {"type": "HAS_ACCESS_TO", "permanent_id": 29},
|
||||||
|
(21, 25, 30): {"type": "HAS_ACCESS_TO", "permanent_id": 30},
|
||||||
|
(21, 26, 31): {"type": "HAS_ACCESS_TO", "permanent_id": 31},
|
||||||
|
(22, 5, 33): {"type": "IS_PART_OF", "permanent_id": 33},
|
||||||
|
(22, 9, 34): {"type": "HAS_ACCESS_TO", "permanent_id": 34},
|
||||||
|
(22, 21, 32): {"type": "IS_PART_OF", "permanent_id": 32},
|
||||||
|
(26, 7, 35): {"type": "IS_STORED_IN", "permanent_id": 35},
|
||||||
|
(26, 8, 36): {"type": "IS_STORED_IN", "permanent_id": 36},
|
||||||
|
}
|
||||||
|
|
||||||
|
example_graph = nx.MultiDiGraph(GRAPH_DATA)
|
||||||
|
nx.set_node_attributes(example_graph, NODE_INFO)
|
||||||
|
nx.set_edge_attributes(example_graph, EDGE_INFO)
|
||||||
|
|
||||||
|
if not is_write:
|
||||||
|
example_graph = nx.freeze(example_graph)
|
||||||
|
|
||||||
|
return mgp_mock.ProcCtx(_mgp_mock.Graph(example_graph))
|
||||||
|
|
||||||
|
|
||||||
|
def get_vertex(ctx, permanent_id: int) -> mgp.Vertex:
|
||||||
|
for vertex in ctx.graph.vertices:
|
||||||
|
if vertex.properties["permanent_id"] == permanent_id:
|
||||||
|
return vertex
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def get_edge(ctx: mgp.ProcCtx, permanent_id: int) -> mgp.Edge:
|
||||||
|
for vertex in ctx.graph.vertices:
|
||||||
|
for edge in vertex.out_edges:
|
||||||
|
if edge.properties["permanent_id"] == permanent_id:
|
||||||
|
return edge
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def get_mock_edge(ctx: mgp_mock.ProcCtx, id: int) -> mgp_mock.Edge:
|
||||||
|
for vertex in ctx.graph.vertices:
|
||||||
|
for edge in vertex.out_edges:
|
||||||
|
if edge.id == id:
|
||||||
|
return edge
|
||||||
|
|
||||||
|
return None
|
101
tests/e2e/mock_api/procedures/vertex.py
Normal file
101
tests/e2e/mock_api/procedures/vertex.py
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
import typing
|
||||||
|
|
||||||
|
import mgp
|
||||||
|
import mgp_mock
|
||||||
|
import test_utils
|
||||||
|
|
||||||
|
|
||||||
|
@mgp.read_proc
|
||||||
|
def compare_apis(ctx: mgp.ProcCtx) -> mgp.Record(results_dict=mgp.Map):
|
||||||
|
mock_ctx = test_utils.get_mock_proc_ctx(is_write=False)
|
||||||
|
results = dict()
|
||||||
|
|
||||||
|
ID = 1
|
||||||
|
|
||||||
|
target_vertex = test_utils.get_vertex(ctx, permanent_id=ID)
|
||||||
|
target_mock_vertex = mock_ctx.graph.get_vertex_by_id(ID)
|
||||||
|
|
||||||
|
results["is_valid"] = test_utils.all_equal(
|
||||||
|
target_vertex.is_valid(),
|
||||||
|
target_mock_vertex.is_valid(),
|
||||||
|
True,
|
||||||
|
)
|
||||||
|
|
||||||
|
results["underlying_graph_is_mutable"] = test_utils.all_equal(
|
||||||
|
target_vertex.underlying_graph_is_mutable(),
|
||||||
|
target_mock_vertex.underlying_graph_is_mutable(),
|
||||||
|
False,
|
||||||
|
)
|
||||||
|
|
||||||
|
results["id"] = test_utils.all_equal(
|
||||||
|
isinstance(target_vertex.id, int),
|
||||||
|
isinstance(target_mock_vertex.id, int),
|
||||||
|
True,
|
||||||
|
)
|
||||||
|
|
||||||
|
results["labels"] = test_utils.all_equal(
|
||||||
|
isinstance(target_vertex.labels, typing.Tuple),
|
||||||
|
isinstance(target_mock_vertex.labels, typing.Tuple),
|
||||||
|
True,
|
||||||
|
) and test_utils.all_equal(
|
||||||
|
{label.name for label in target_vertex.labels},
|
||||||
|
{mock_label.name for mock_label in target_mock_vertex.labels},
|
||||||
|
{"Team"},
|
||||||
|
)
|
||||||
|
|
||||||
|
results["properties"] = test_utils.all_equal(
|
||||||
|
isinstance(target_vertex.properties, mgp.Properties),
|
||||||
|
isinstance(target_mock_vertex.properties, mgp_mock.Properties),
|
||||||
|
True,
|
||||||
|
) and test_utils.all_equal(
|
||||||
|
{prop for prop in target_vertex.properties},
|
||||||
|
{mock_prop for mock_prop in target_mock_vertex.properties},
|
||||||
|
{"name", "permanent_id"},
|
||||||
|
)
|
||||||
|
|
||||||
|
results["in_edges"] = test_utils.all_equal(
|
||||||
|
all(isinstance(edge, mgp.Edge) for edge in target_vertex.in_edges),
|
||||||
|
all(isinstance(edge, mgp_mock.Edge) for edge in target_mock_vertex.in_edges),
|
||||||
|
True,
|
||||||
|
) and test_utils.all_equal(
|
||||||
|
{edge.properties["permanent_id"] for edge in target_vertex.in_edges},
|
||||||
|
{edge.properties["permanent_id"] for edge in target_mock_vertex.in_edges},
|
||||||
|
{0, 9, 15, 37},
|
||||||
|
)
|
||||||
|
|
||||||
|
results["out_edges"] = test_utils.all_equal(
|
||||||
|
all(isinstance(edge, mgp.Edge) for edge in target_vertex.out_edges),
|
||||||
|
all(isinstance(edge, mgp_mock.Edge) for edge in target_mock_vertex.out_edges),
|
||||||
|
True,
|
||||||
|
) and test_utils.all_equal(
|
||||||
|
{edge.properties["permanent_id"] for edge in target_vertex.out_edges},
|
||||||
|
{edge.properties["permanent_id"] for edge in target_mock_vertex.out_edges},
|
||||||
|
{4, 5, 6, 7, 8},
|
||||||
|
)
|
||||||
|
|
||||||
|
ID_2 = 2
|
||||||
|
|
||||||
|
target_vertex_2 = test_utils.get_vertex(ctx, permanent_id=ID_2)
|
||||||
|
target_mock_vertex_2 = mock_ctx.graph.get_vertex_by_id(ID_2)
|
||||||
|
|
||||||
|
results["__eq__"] = test_utils.all_equal(
|
||||||
|
target_vertex == target_vertex,
|
||||||
|
target_mock_vertex == target_mock_vertex,
|
||||||
|
True,
|
||||||
|
) and test_utils.all_equal(
|
||||||
|
target_vertex == target_vertex_2,
|
||||||
|
target_mock_vertex == target_mock_vertex_2,
|
||||||
|
False,
|
||||||
|
)
|
||||||
|
|
||||||
|
results["__ne__"] = test_utils.all_equal(
|
||||||
|
target_vertex != target_vertex_2,
|
||||||
|
target_mock_vertex != target_mock_vertex_2,
|
||||||
|
True,
|
||||||
|
) and test_utils.all_equal(
|
||||||
|
target_vertex != target_vertex,
|
||||||
|
target_mock_vertex != target_mock_vertex,
|
||||||
|
False,
|
||||||
|
)
|
||||||
|
|
||||||
|
return mgp.Record(results_dict=results)
|
32
tests/e2e/mock_api/procedures/vertices.py
Normal file
32
tests/e2e/mock_api/procedures/vertices.py
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import mgp
|
||||||
|
import mgp_mock
|
||||||
|
import test_utils
|
||||||
|
|
||||||
|
|
||||||
|
@mgp.read_proc
|
||||||
|
def compare_apis(ctx: mgp.ProcCtx) -> mgp.Record(results_dict=mgp.Map):
|
||||||
|
mock_ctx = test_utils.get_mock_proc_ctx(is_write=False)
|
||||||
|
results = dict()
|
||||||
|
|
||||||
|
vertices = ctx.graph.vertices
|
||||||
|
mock_vertices = mock_ctx.graph.vertices
|
||||||
|
|
||||||
|
results["is_valid"] = test_utils.all_equal(
|
||||||
|
vertices.is_valid(),
|
||||||
|
mock_vertices.is_valid(),
|
||||||
|
True,
|
||||||
|
)
|
||||||
|
|
||||||
|
results["__iter__"] = test_utils.all_equal(
|
||||||
|
all(isinstance(vertex, mgp.Vertex) for vertex in vertices),
|
||||||
|
all(isinstance(vertex, mgp_mock.Vertex) for vertex in mock_vertices),
|
||||||
|
True,
|
||||||
|
)
|
||||||
|
|
||||||
|
results["__len__"] = test_utils.all_equal(
|
||||||
|
len(vertices),
|
||||||
|
len(mock_vertices),
|
||||||
|
27,
|
||||||
|
)
|
||||||
|
|
||||||
|
return mgp.Record(results_dict=results)
|
195
tests/e2e/mock_api/test_compare_mock.py
Normal file
195
tests/e2e/mock_api/test_compare_mock.py
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from common import connect, execute_and_fetch_results_dict
|
||||||
|
|
||||||
|
|
||||||
|
def test_label():
|
||||||
|
expected_results = {
|
||||||
|
"name": True,
|
||||||
|
"__eq__": True,
|
||||||
|
"__ne__": True,
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor = connect().cursor()
|
||||||
|
results = execute_and_fetch_results_dict(
|
||||||
|
cursor, "CALL label.compare_apis() YIELD results_dict RETURN results_dict;"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert results == expected_results
|
||||||
|
|
||||||
|
|
||||||
|
def test_properties_on_vertex():
|
||||||
|
expected_results = {
|
||||||
|
"get": True,
|
||||||
|
"get[default]": True,
|
||||||
|
"set": True,
|
||||||
|
"items": True,
|
||||||
|
"keys": True,
|
||||||
|
"values": True,
|
||||||
|
"__len__": True,
|
||||||
|
"__iter__": True,
|
||||||
|
"__getitem__": True,
|
||||||
|
"__setitem__": True,
|
||||||
|
"__contains__": True,
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor = connect().cursor()
|
||||||
|
results = execute_and_fetch_results_dict(
|
||||||
|
cursor, "CALL properties.compare_apis_on_vertex() YIELD results_dict RETURN results_dict;"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert results == expected_results
|
||||||
|
|
||||||
|
|
||||||
|
def test_properties_on_edge():
|
||||||
|
expected_results = {
|
||||||
|
"get": True,
|
||||||
|
"get[default]": True,
|
||||||
|
"set": True,
|
||||||
|
"items": True,
|
||||||
|
"keys": True,
|
||||||
|
"values": True,
|
||||||
|
"__len__": True,
|
||||||
|
"__iter__": True,
|
||||||
|
"__getitem__": True,
|
||||||
|
"__setitem__": True,
|
||||||
|
"__contains__": True,
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor = connect().cursor()
|
||||||
|
results = execute_and_fetch_results_dict(
|
||||||
|
cursor, "CALL properties.compare_apis_on_edge() YIELD results_dict RETURN results_dict;"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert results == expected_results
|
||||||
|
|
||||||
|
|
||||||
|
def test_edge_type():
|
||||||
|
expected_results = {
|
||||||
|
"name": True,
|
||||||
|
"__eq__": True,
|
||||||
|
"__ne__": True,
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor = connect().cursor()
|
||||||
|
results = execute_and_fetch_results_dict(
|
||||||
|
cursor, "CALL edge_type.compare_apis() YIELD results_dict RETURN results_dict;"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert results == expected_results
|
||||||
|
|
||||||
|
|
||||||
|
def test_edge():
|
||||||
|
expected_results = {
|
||||||
|
"is_valid": True,
|
||||||
|
"underlying_graph_is_mutable": True,
|
||||||
|
"id": True,
|
||||||
|
"type": True,
|
||||||
|
"from_vertex": True,
|
||||||
|
"to_vertex": True,
|
||||||
|
"properties": True,
|
||||||
|
"__eq__": True,
|
||||||
|
"__ne__": True,
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor = connect().cursor()
|
||||||
|
results = execute_and_fetch_results_dict(cursor, "CALL edge.compare_apis() YIELD results_dict RETURN results_dict;")
|
||||||
|
|
||||||
|
assert results == expected_results
|
||||||
|
|
||||||
|
|
||||||
|
def test_vertex():
|
||||||
|
expected_results = {
|
||||||
|
"is_valid": True,
|
||||||
|
"underlying_graph_is_mutable": True,
|
||||||
|
"id": True,
|
||||||
|
"labels": True,
|
||||||
|
"properties": True,
|
||||||
|
"in_edges": True,
|
||||||
|
"out_edges": True,
|
||||||
|
"__eq__": True,
|
||||||
|
"__ne__": True,
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor = connect().cursor()
|
||||||
|
results = execute_and_fetch_results_dict(
|
||||||
|
cursor, "CALL vertex.compare_apis() YIELD results_dict RETURN results_dict;"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert results == expected_results
|
||||||
|
|
||||||
|
|
||||||
|
def test_path():
|
||||||
|
expected_results = {
|
||||||
|
"__copy__": True,
|
||||||
|
"is_valid": True,
|
||||||
|
"expand": True,
|
||||||
|
"vertices": True,
|
||||||
|
"edges": True,
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor = connect().cursor()
|
||||||
|
results = execute_and_fetch_results_dict(cursor, "CALL path.compare_apis() YIELD results_dict RETURN results_dict;")
|
||||||
|
|
||||||
|
assert results == expected_results
|
||||||
|
|
||||||
|
|
||||||
|
def test_record():
|
||||||
|
expected_results = {
|
||||||
|
"fields": True,
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor = connect().cursor()
|
||||||
|
results = execute_and_fetch_results_dict(
|
||||||
|
cursor, "CALL record.compare_apis() YIELD results_dict RETURN results_dict;"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert results == expected_results
|
||||||
|
|
||||||
|
|
||||||
|
def test_vertices():
|
||||||
|
expected_results = {
|
||||||
|
"is_valid": True,
|
||||||
|
"__iter__": True,
|
||||||
|
"__len__": True,
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor = connect().cursor()
|
||||||
|
results = execute_and_fetch_results_dict(
|
||||||
|
cursor, "CALL vertices.compare_apis() YIELD results_dict RETURN results_dict;"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert results == expected_results
|
||||||
|
|
||||||
|
|
||||||
|
def test_graph():
|
||||||
|
expected_results = {
|
||||||
|
"create_edge": True,
|
||||||
|
"create_vertex": True,
|
||||||
|
"delete_edge": True,
|
||||||
|
"delete_vertex": True,
|
||||||
|
"detach_delete_vertex": True,
|
||||||
|
"edge_id_assignment": True,
|
||||||
|
"get_vertex_by_id": True,
|
||||||
|
"is_mutable": True,
|
||||||
|
"is_not_mutable": True,
|
||||||
|
"is_valid": True,
|
||||||
|
"vertices": True,
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor = connect().cursor()
|
||||||
|
results = execute_and_fetch_results_dict(
|
||||||
|
cursor, "CALL graph.compare_apis() YIELD results_dict RETURN results_dict;"
|
||||||
|
)
|
||||||
|
results.update(
|
||||||
|
execute_and_fetch_results_dict(
|
||||||
|
cursor, "CALL graph.test_read_proc_mutability() YIELD results_dict RETURN results_dict;"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
assert results == expected_results
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(pytest.main([__file__, "-rA"]))
|
83
tests/e2e/mock_api/workloads.yaml
Normal file
83
tests/e2e/mock_api/workloads.yaml
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
compare_mock: &compare_mock
|
||||||
|
cluster:
|
||||||
|
main:
|
||||||
|
args: ["--bolt-port", "7687", "--log-level=TRACE", "--also-log-to-stderr"]
|
||||||
|
log_file: "test-compare-mock-e2e.log"
|
||||||
|
setup_queries:
|
||||||
|
- "CREATE INDEX ON :__mg_vertex__(__mg_id__);"
|
||||||
|
- "CREATE (:__mg_vertex__:`Person` {__mg_id__: 0, `name`: 'Peter', `surname`: 'Yang'});"
|
||||||
|
- "CREATE (:__mg_vertex__:`Team` {__mg_id__: 1, `name`: 'Engineering'});"
|
||||||
|
- "CREATE (:__mg_vertex__:`Repository` {__mg_id__: 2, `name`: 'Memgraph'});"
|
||||||
|
- "CREATE (:__mg_vertex__:`Repository` {__mg_id__: 3, `name`: 'MAGE'});"
|
||||||
|
- "CREATE (:__mg_vertex__:`Repository` {__mg_id__: 4, `name`: 'GQLAlchemy'});"
|
||||||
|
- "CREATE (:__mg_vertex__:`Company`:`Startup` {__mg_id__: 5, `name`: 'Memgraph'});"
|
||||||
|
- "CREATE (:__mg_vertex__:`File` {__mg_id__: 6, `name`: 'welcome_to_engineering.txt'});"
|
||||||
|
- "CREATE (:__mg_vertex__:`Storage` {__mg_id__: 7, `name`: 'Google Drive'});"
|
||||||
|
- "CREATE (:__mg_vertex__:`Storage` {__mg_id__: 8, `name`: 'Notion'});"
|
||||||
|
- "CREATE (:__mg_vertex__:`File` {__mg_id__: 9, `name`: 'welcome_to_memgraph.txt'});"
|
||||||
|
- "CREATE (:__mg_vertex__:`Person` {__mg_id__: 10, `name`: 'Carl'});"
|
||||||
|
- "CREATE (:__mg_vertex__:`Folder` {__mg_id__: 11, `name`: 'engineering_folder'});"
|
||||||
|
- "CREATE (:__mg_vertex__:`Person` {__mg_id__: 12, `name`: 'Anna'});"
|
||||||
|
- "CREATE (:__mg_vertex__:`Folder` {__mg_id__: 13, `name`: 'operations_folder'});"
|
||||||
|
- "CREATE (:__mg_vertex__:`Team` {__mg_id__: 14, `name`: 'Operations'});"
|
||||||
|
- "CREATE (:__mg_vertex__:`File` {__mg_id__: 15, `name`: 'operations101.txt'});"
|
||||||
|
- "CREATE (:__mg_vertex__:`File` {__mg_id__: 16, `name`: 'expenses2022.csv'});"
|
||||||
|
- "CREATE (:__mg_vertex__:`File` {__mg_id__: 17, `name`: 'salaries2022.csv'});"
|
||||||
|
- "CREATE (:__mg_vertex__:`File` {__mg_id__: 18, `name`: 'engineering101.txt'});"
|
||||||
|
- "CREATE (:__mg_vertex__:`File` {__mg_id__: 19, `name`: 'working_with_github.txt'});"
|
||||||
|
- "CREATE (:__mg_vertex__:`File` {__mg_id__: 20, `name`: 'working_with_notion.txt'});"
|
||||||
|
- "CREATE (:__mg_vertex__:`Team` {__mg_id__: 21, `name`: 'Marketing'});"
|
||||||
|
- "CREATE (:__mg_vertex__:`Person` {__mg_id__: 22, `name`: 'Julie'});"
|
||||||
|
- "CREATE (:__mg_vertex__:`Account` {__mg_id__: 23, `name`: 'Facebook'});"
|
||||||
|
- "CREATE (:__mg_vertex__:`Account` {__mg_id__: 24, `name`: 'LinkedIn'});"
|
||||||
|
- "CREATE (:__mg_vertex__:`Account` {__mg_id__: 25, `name`: 'HackerNews'});"
|
||||||
|
- "CREATE (:__mg_vertex__:`File` {__mg_id__: 26, `name`: 'welcome_to_marketing.txt'});"
|
||||||
|
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 0 AND v.__mg_id__ = 1 CREATE (u)-[:`IS_PART_OF` {`permanent_id`: 0}]->(v);"
|
||||||
|
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 0 AND v.__mg_id__ = 5 CREATE (u)-[:`IS_PART_OF` {`permanent_id`: 1}]->(v);"
|
||||||
|
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 0 AND v.__mg_id__ = 9 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 2}]->(v);"
|
||||||
|
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 0 AND v.__mg_id__ = 14 CREATE (u)-[:`IS_PART_OF` {`permanent_id`: 3}]->(v);"
|
||||||
|
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 1 AND v.__mg_id__ = 2 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 4}]->(v);"
|
||||||
|
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 1 AND v.__mg_id__ = 3 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 5}]->(v);"
|
||||||
|
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 1 AND v.__mg_id__ = 4 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 6}]->(v);"
|
||||||
|
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 1 AND v.__mg_id__ = 6 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 7}]->(v);"
|
||||||
|
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 1 AND v.__mg_id__ = 11 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 8}]->(v);"
|
||||||
|
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 5 AND v.__mg_id__ = 1 CREATE (u)-[:`HAS_TEAM` {`permanent_id`: 9}]->(v);"
|
||||||
|
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 5 AND v.__mg_id__ = 21 CREATE (u)-[:`HAS_TEAM` {`permanent_id`: 10}]->(v);"
|
||||||
|
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 5 AND v.__mg_id__ = 14 CREATE (u)-[:`HAS_TEAM` {`permanent_id`: 11}]->(v);"
|
||||||
|
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 6 AND v.__mg_id__ = 7 CREATE (u)-[:`IS_STORED_IN` {`permanent_id`: 12}]->(v);"
|
||||||
|
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 6 AND v.__mg_id__ = 8 CREATE (u)-[:`IS_STORED_IN` {`permanent_id`: 13}]->(v);"
|
||||||
|
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 9 AND v.__mg_id__ = 12 CREATE (u)-[:`CREATED_BY` {`permanent_id`: 14}]->(v);"
|
||||||
|
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 10 AND v.__mg_id__ = 1 CREATE (u)-[:`IS_PART_OF` {`permanent_id`: 15}]->(v);"
|
||||||
|
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 10 AND v.__mg_id__ = 5 CREATE (u)-[:`IS_PART_OF` {`permanent_id`: 16}]->(v);"
|
||||||
|
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 10 AND v.__mg_id__ = 9 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 17}]->(v);"
|
||||||
|
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 11 AND v.__mg_id__ = 7 CREATE(u)-[:`IS_STORED_IN` {`permanent_id`: 18}]->(v);"
|
||||||
|
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 11 AND v.__mg_id__ = 18 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 19}]->(v);"
|
||||||
|
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 11 AND v.__mg_id__ = 19 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 20}]->(v);"
|
||||||
|
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 11 AND v.__mg_id__ = 20 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 21}]->(v);"
|
||||||
|
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 12 AND v.__mg_id__ = 14 CREATE (u)-[:`IS_PART_OF` {`permanent_id`: 22}]->(v);"
|
||||||
|
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 13 AND v.__mg_id__ = 15 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 23}]->(v);"
|
||||||
|
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 13 AND v.__mg_id__ = 16 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 24}]->(v);"
|
||||||
|
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 13 AND v.__mg_id__ = 17 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 25}]->(v);"
|
||||||
|
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 13 AND v.__mg_id__ = 7 CREATE (u)-[:`IS_STORED_IN` {`permanent_id`: 26}]->(v);"
|
||||||
|
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 13 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 27}]->(v);"
|
||||||
|
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 21 AND v.__mg_id__ = 23 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 28}]->(v);"
|
||||||
|
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 21 AND v.__mg_id__ = 24 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 29}]->(v);"
|
||||||
|
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 21 AND v.__mg_id__ = 25 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 30}]->(v);"
|
||||||
|
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 21 AND v.__mg_id__ = 26 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 31}]->(v);"
|
||||||
|
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 22 AND v.__mg_id__ = 21 CREATE (u)-[:`IS_PART_OF` {`permanent_id`: 32}]->(v);"
|
||||||
|
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 22 AND v.__mg_id__ = 5 CREATE (u)-[:`IS_PART_OF` {`permanent_id`: 33}]->(v);"
|
||||||
|
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 22 AND v.__mg_id__ = 9 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 34}]->(v);"
|
||||||
|
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 26 AND v.__mg_id__ = 7 CREATE (u)-[:`IS_STORED_IN` {`permanent_id`: 35}]->(v);"
|
||||||
|
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 26 AND v.__mg_id__ = 8 CREATE (u)-[:`IS_STORED_IN` {`permanent_id`: 36}]->(v);"
|
||||||
|
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 5 AND v.__mg_id__ = 1 CREATE (u)-[:`HAS_TEAM_2` {`importance`: 'HIGH', `permanent_id`: 37}]->(v);"
|
||||||
|
- "DROP INDEX ON :__mg_vertex__(__mg_id__);"
|
||||||
|
- "MATCH (u) SET u.permanent_id = u.__mg_id__;"
|
||||||
|
- "MATCH (u) REMOVE u:__mg_vertex__, u.__mg_id__;"
|
||||||
|
validation_queries: []
|
||||||
|
|
||||||
|
workloads:
|
||||||
|
- name: "test-compare-mock" # should be the same as the python file
|
||||||
|
binary: "tests/e2e/pytest_runner.sh"
|
||||||
|
proc: "tests/e2e/mock_api/procedures/"
|
||||||
|
args: ["mock_api/test_compare_mock.py"]
|
||||||
|
<<: *compare_mock
|
@ -15,6 +15,7 @@ PIP_DEPS=(
|
|||||||
"pytest==6.2.3"
|
"pytest==6.2.3"
|
||||||
"pyyaml==5.4.1"
|
"pyyaml==5.4.1"
|
||||||
"six==1.15.0"
|
"six==1.15.0"
|
||||||
|
"networkx==2.4"
|
||||||
)
|
)
|
||||||
cd "$DIR"
|
cd "$DIR"
|
||||||
|
|
||||||
|
@ -6,3 +6,4 @@ python-dateutil==2.6.1
|
|||||||
pytz==2017.2
|
pytz==2017.2
|
||||||
six==1.11.0
|
six==1.11.0
|
||||||
tabulate==0.8.1
|
tabulate==0.8.1
|
||||||
|
networkx==2.4
|
||||||
|
Loading…
Reference in New Issue
Block a user