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

Adds tests so that Neo4jGraph class now has 100% coverage #21

Merged
10 changes: 5 additions & 5 deletions libs/neo4j/langchain_neo4j/graphs/neo4j_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ def _format_schema(schema: Dict, is_enhanced: bool) -> str:
"DATE_TIME",
"LOCAL_DATE_TIME",
]:
if prop.get("min") is not None:
if prop.get("min") and prop.get("max"):
example = f'Min: {prop["min"]}, Max: {prop["max"]}'
else:
example = (
Expand All @@ -215,7 +215,7 @@ def _format_schema(schema: Dict, is_enhanced: bool) -> str:
formatted_rel_props.append(f"- **{rel_type}**")
for prop in properties:
example = ""
if prop["type"] == "STRING":
if prop["type"] == "STRING" and prop.get("values"):
if prop.get("distinct_count", 11) > DISTINCT_VALUE_LIMIT:
example = (
f'Example: "{clean_string_values(prop["values"][0])}"'
Expand All @@ -238,8 +238,8 @@ def _format_schema(schema: Dict, is_enhanced: bool) -> str:
"DATE_TIME",
"LOCAL_DATE_TIME",
]:
if prop.get("min"): # If we have min/max
example = f'Min: {prop["min"]}, Max: {prop["max"]}'
if prop.get("min") and prop.get("max"): # If we have min/max
example = f'Min: {prop["min"]}, Max: {prop["max"]}'
else: # return a single value
example = (
f'Example: "{prop["values"][0]}"' if prop["values"] else ""
Expand All @@ -252,7 +252,7 @@ def _format_schema(schema: Dict, is_enhanced: bool) -> str:
f'Min Size: {prop["min_size"]}, Max Size: {prop["max_size"]}'
)
formatted_rel_props.append(
f" - `{prop['property']}: {prop['type']}` {example}"
f" - `{prop['property']}`: {prop['type']} {example}"
)
else:
# Format node properties
Expand Down
89 changes: 83 additions & 6 deletions libs/neo4j/tests/integration_tests/graphs/test_neo4j.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import os
import urllib

import pytest
from langchain_core.documents import Document

from langchain_neo4j import Neo4jGraph
Expand All @@ -19,6 +21,7 @@
source=Node(id="foo", type="foo"),
target=Node(id="bar", type="bar"),
type="REL",
properties={"key": "val"},
)
],
source=Document(page_content="source document"),
Expand Down Expand Up @@ -130,7 +133,7 @@ def test_neo4j_timeout() -> None:


def test_neo4j_sanitize_values() -> None:
"""Test that neo4j uses the timeout correctly."""
"""Test that lists with more than 128 elements are removed from the results."""
url = os.environ.get("NEO4J_URI", "bolt://localhost:7687")
username = os.environ.get("NEO4J_USERNAME", "neo4j")
password = os.environ.get("NEO4J_PASSWORD", "pleaseletmein")
Expand Down Expand Up @@ -347,7 +350,16 @@ def test_enhanced_schema() -> None:
}
],
},
"rel_props": {},
"rel_props": {
"REL": [
{
"distinct_count": 1,
"property": "key",
"type": "STRING",
"values": ["val"],
}
]
},
"relationships": [{"start": "foo", "type": "REL", "end": "bar"}],
}
# remove metadata portion of schema
Expand All @@ -365,16 +377,37 @@ def test_enhanced_schema_exception() -> None:
assert password is not None

graph = Neo4jGraph(
url=url, username=username, password=password, enhanced_schema=True
url=url,
username=username,
password=password,
enhanced_schema=True,
refresh_schema=False,
)
graph.query("MATCH (n) DETACH DELETE n")
graph.query("CREATE (:Node {foo:'bar'})," "(:Node {foo: 1}), (:Node {foo: [1,2]})")
graph.query(
"CREATE (:Node {foo: 'bar'}), (:Node {foo: 1}), (:Node {foo: [1,2]}), "
"(: EmptyNode)"
)
graph.query(
"MATCH (a:Node {foo: 'bar'}), (b:Node {foo: 1}), "
"(c:Node {foo: [1,2]}), (d: EmptyNode) "
"CREATE (a)-[:REL {foo: 'bar'}]->(b), (b)-[:REL {foo: 1}]->(c), "
"(c)-[:REL {foo: [1,2]}]->(a), (d)-[:EMPTY_REL {}]->(d)"
)
graph.refresh_schema()
expected_output = {
"node_props": {"Node": [{"property": "foo", "type": "STRING"}]},
"rel_props": {},
"relationships": [],
"rel_props": {"REL": [{"property": "foo", "type": "STRING"}]},
"relationships": [
{
"end": "Node",
"start": "Node",
"type": "REL",
},
{"end": "EmptyNode", "start": "EmptyNode", "type": "EMPTY_REL"},
],
}

# remove metadata portion of schema
del graph.structured_schema["metadata"]
assert graph.structured_schema == expected_output
Expand Down Expand Up @@ -558,3 +591,47 @@ def test_neo4j_multiple_close() -> None:
# Test that multiple closes don't raise errors
graph.close()
graph.close() # This should not raise an error


def test_invalid_url() -> None:
"""Test initializing with invalid credentials raises ValueError."""
url = os.environ.get("NEO4J_URI", "bolt://localhost:7687")
username = os.environ.get("NEO4J_USERNAME", "neo4j")
password = os.environ.get("NEO4J_PASSWORD", "pleaseletmein")
assert url is not None
assert username is not None
assert password is not None

# Parse the original URL
parsed_url = urllib.parse.urlparse(url)
# Increment the port number by 1 and wrap around if necessary
original_port = parsed_url.port or 7687
new_port = (original_port + 1) % 65535 or 1
# Reconstruct the netloc (hostname:port)
new_netloc = f"{parsed_url.hostname}:{new_port}"
# Rebuild the URL with the new netloc
new_url = parsed_url._replace(netloc=new_netloc).geturl()

with pytest.raises(ValueError) as exc_info:
Neo4jGraph(
url=new_url,
username=username,
password=password,
)
assert "Please ensure that the url is correct" in str(exc_info.value)


def test_invalid_credentials() -> None:
"""Test initializing with invalid credentials raises ValueError."""
url = os.environ.get("NEO4J_URI", "bolt://localhost:7687")
assert url is not None

with pytest.raises(ValueError) as exc_info:
Neo4jGraph(
url=url,
username="invalid_username",
password="invalid_password",
)
assert "Please ensure that the username and password are correct" in str(
exc_info.value
)
Loading
Loading