Skip to content

Commit e71d643

Browse files
committed
Added statement result tck tests
1 parent 0e838f3 commit e71d643

12 files changed

+344
-132
lines changed

neo4j/v1/session.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ def keys(self):
241241
# Fetch messages until we have the header or a failure
242242
while self._keys is None and not self._consumed:
243243
self.connection.fetch()
244-
return self._keys
244+
return tuple(self._keys)
245245

246246
def buffer(self):
247247
if self.connection and not self.connection.closed:
@@ -264,9 +264,9 @@ def single(self):
264264
records = list(self)
265265
num_records = len(records)
266266
if num_records == 0:
267-
raise ResultError("No records found in stream")
267+
raise ResultError("Cannot retrieve a single record, because this result is empty.")
268268
elif num_records != 1:
269-
raise ResultError("Multiple records found in stream")
269+
raise ResultError("Expected a result with a single record, but this result contains at least one more.")
270270
else:
271271
return records[0]
272272

@@ -478,16 +478,19 @@ def healthy(self):
478478
return None if connection.closed else not connection.defunct
479479

480480
def run(self, statement, parameters=None):
481+
if self.transaction:
482+
raise ProtocolError("Please close the currently open transaction object before running more "
483+
"statements/transactions in the current session.")
484+
return self._run(statement, parameters)
485+
486+
def _run(self, statement, parameters=None):
481487
""" Run a parameterised Cypher statement.
482488
483489
:param statement: Cypher statement to execute
484490
:param parameters: dictionary of parameters
485491
:return: Cypher result
486492
:rtype: :class:`.StatementResult`
487493
"""
488-
if self.transaction:
489-
raise ProtocolError("Please close the currently open transaction object before running more "
490-
"statements/transactions in the current session.")
491494

492495
# Ensure the statement is a Unicode value
493496
if isinstance(statement, bytes):
@@ -521,6 +524,8 @@ def close(self):
521524
"""
522525
if self.last_result:
523526
self.last_result.buffer()
527+
if self.transaction:
528+
self.transaction.close()
524529
self.driver.recycle(self)
525530

526531
def begin_transaction(self):
@@ -558,7 +563,7 @@ class Transaction(object):
558563

559564
def __init__(self, session):
560565
self.session = session
561-
self.session.run("BEGIN")
566+
self.session._run("BEGIN")
562567

563568
def __enter__(self):
564569
return self
@@ -576,7 +581,7 @@ def run(self, statement, parameters=None):
576581
:return:
577582
"""
578583
assert not self.closed
579-
return self.session.run(statement, parameters)
584+
return self.session._run(statement, parameters)
580585

581586
def commit(self):
582587
""" Mark this transaction as successful and close in order to
@@ -597,9 +602,9 @@ def close(self):
597602
"""
598603
assert not self.closed
599604
if self.success:
600-
self.session.run("COMMIT")
605+
self.session._run("COMMIT")
601606
else:
602-
self.session.run("ROLLBACK")
607+
self.session._run("ROLLBACK")
603608
self.closed = True
604609
self.session.transaction = None
605610

runtests.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ echo ""
8383

8484
TEST_RUNNER="coverage run -m ${UNITTEST} discover -vfs ${TEST}"
8585
EXAMPLES_RUNNER="coverage run -m ${UNITTEST} discover -vfs examples"
86-
BEHAVE_RUNNER="behave --tags=-db --tags=-in_dev test/tck"
86+
BEHAVE_RUNNER="behave --tags=-db --tags=-in_dev --tags=-fixed_session_pool test/tck"
8787

8888
if [ ${RUNNING} -eq 1 ]
8989
then
@@ -112,4 +112,4 @@ else
112112

113113
fi
114114

115-
fi
115+
fi

test/tck/TestValue.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
#!/usr/bin/env python
2+
# -*- encoding: utf-8 -*-
3+
4+
# Copyright (c) 2002-2016 "Neo Technology,"
5+
# Network Engine for Objects in Lund AB [http://neotechnology.com]
6+
#
7+
# This file is part of Neo4j.
8+
#
9+
# Licensed under the Apache License, Version 2.0 (the "License");
10+
# you may not use this file except in compliance with the License.
11+
# You may obtain a copy of the License at
12+
#
13+
# http://www.apache.org/licenses/LICENSE-2.0
14+
#
15+
# Unless required by applicable law or agreed to in writing, software
16+
# distributed under the License is distributed on an "AS IS" BASIS,
17+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18+
# See the License for the specific language governing permissions and
19+
# limitations under the License.
20+
21+
from neo4j.v1 import Node, Relationship, Path
22+
from neo4j.v1 import string
23+
24+
25+
class TestValue:
26+
content = None
27+
28+
def __init__(self, entity):
29+
self.content = {}
30+
if isinstance(entity, Node):
31+
self.content = self.create_node(entity)
32+
elif isinstance(entity, Relationship):
33+
self.content = self.create_relationship(entity)
34+
elif isinstance(entity, Path):
35+
self.content = self.create_path(entity)
36+
elif isinstance(entity, int) or isinstance(entity, float) or isinstance(entity,
37+
(str, string)) or entity is None:
38+
self.content['value'] = entity
39+
else:
40+
raise ValueError("Do not support object type: %s" % entity)
41+
42+
def __hash__(self):
43+
return hash(repr(self))
44+
45+
def __eq__(self, other):
46+
return self.content == other.content
47+
48+
def __ne__(self, other):
49+
return not self.__eq__(other)
50+
51+
def __repr__(self):
52+
return str(self.content)
53+
54+
def create_node(self, entity):
55+
content = {'properties': entity.properties, 'labels': entity.labels, 'obj': "node"}
56+
57+
return content
58+
59+
def create_path(self, entity):
60+
content = {}
61+
prev_id = entity.start.id
62+
p = []
63+
for i, rel in enumerate(list(entity)):
64+
n = entity.nodes[i + 1]
65+
current_id = n.id
66+
if rel.start == prev_id and rel.end == current_id:
67+
rel.start = i
68+
rel.end = i + 1
69+
elif rel.start == current_id and rel.end == prev_id:
70+
rel.start = i + 1
71+
rel.end = i
72+
else:
73+
raise ValueError(
74+
"Relationships end and start should point to surrounding nodes. Rel: %s N1id: %s N2id: %s. At entity#%s" % (
75+
rel, current_id, prev_id, i))
76+
p += [self.create_relationship(rel, True), self.create_node(n)]
77+
prev_id = current_id
78+
content['path'] = p
79+
content['obj'] = "path"
80+
content['start'] = self.create_node(entity.start)
81+
return content
82+
83+
def create_relationship(self, entity, include_start_end=False):
84+
content = {'obj': "relationship"}
85+
if include_start_end:
86+
self.content['start'] = entity.start
87+
self.content['end'] = entity.end
88+
content['type'] = entity.type
89+
content['properties'] = entity.properties
90+
return content

test/tck/environment.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ def after_all(context):
5959

6060

6161
def after_scenario(context, scenario):
62-
pass
6362
for runner in tck_util.runners:
6463
runner.close()
6564

test/tck/resultparser.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import json
2222
import re
2323
from neo4j.v1 import Node, Relationship, Path
24-
from tck_util import TestValue
24+
from TestValue import TestValue
2525

2626

2727
def parse_values_to_comparable(row):

test/tck/steps/cypher_compability_steps.py

Lines changed: 4 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@
2121
from behave import *
2222

2323
from test.tck import tck_util
24-
from test.tck.tck_util import TestValue
25-
from test.tck.resultparser import parse_values, parse_values_to_comparable
24+
from test.tck.resultparser import parse_values
2625

2726
use_step_matcher("re")
2827

@@ -54,51 +53,10 @@ def step_impl(context, statement):
5453

5554
@then("result")
5655
def step_impl(context):
57-
expected = table_to_comparable_result(context.table)
56+
expected = tck_util.table_to_comparable_result(context.table)
5857
assert(len(context.results) > 0)
5958
for result in context.results:
6059
records = list(result)
61-
given = driver_result_to_comparable_result(records)
62-
if not unordered_equal(given, expected):
60+
given = tck_util.driver_result_to_comparable_result(records)
61+
if not tck_util.unordered_equal(given, expected):
6362
raise Exception("Does not match given: \n%s expected: \n%s" % (given, expected))
64-
65-
66-
def _driver_value_to_comparable(val):
67-
if isinstance(val, list):
68-
l = [_driver_value_to_comparable(v) for v in val]
69-
return l
70-
else:
71-
return TestValue(val)
72-
73-
74-
def table_to_comparable_result(table):
75-
result = []
76-
keys = table.headings
77-
for row in table:
78-
result.append(
79-
{keys[i]: parse_values_to_comparable(row[i]) for i in range(len(row))})
80-
return result
81-
82-
83-
def driver_result_to_comparable_result(result):
84-
records = []
85-
for record in result:
86-
records.append({key: _driver_value_to_comparable(record[key]) for key in record})
87-
return records
88-
89-
90-
def unordered_equal(given, expected):
91-
l1 = given[:]
92-
l2 = expected[:]
93-
assert isinstance(l1, list)
94-
assert isinstance(l2, list)
95-
assert len(l1) == len(l2)
96-
for d1 in l1:
97-
size = len(l2)
98-
for d2 in l2:
99-
if d1 == d2:
100-
l2.remove(d2)
101-
break
102-
if size == len(l2):
103-
return False
104-
return True

test/tck/steps/driver_result_api_steps.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,6 @@ def step_impl(context, expected):
6363
def step_impl(context):
6464
for summary in context.summaries:
6565
for row in context.table:
66-
print(row[0].replace(" ","_"))
67-
print(getattr(summary.counters, row[0].replace(" ","_")))
6866
assert getattr(summary.counters, row[0].replace(" ","_")) == parse_values(row[1])
6967

7068

test/tck/steps/errror_reporting_steps.py renamed to test/tck/steps/error_reporting_steps.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
from neo4j.v1.exceptions import ProtocolError, CypherError
2424
from test.tck import tck_util
2525

26-
from neo4j.v1 import compat, GraphDatabase, basic_auth
26+
from neo4j.v1 import GraphDatabase
2727

2828
use_step_matcher("re")
2929

@@ -45,6 +45,8 @@ def step_impl(context):
4545
context.session.run("CREATE (:n)")
4646
except Exception as e:
4747
context.exception = e
48+
finally:
49+
context.session.close()
4850

4951

5052
@step("I start a new `Transaction` with the same session before closing the previous")
@@ -53,12 +55,16 @@ def step_impl(context):
5355
context.session.begin_transaction()
5456
except Exception as e:
5557
context.exception = e
58+
finally:
59+
context.session.close()
5660

5761

5862
@step("I run a non valid cypher statement")
5963
def step_impl(context):
6064
try:
61-
context.driver.session().run("NOT VALID").consume()
65+
s = context.driver.session()
66+
print(s.transaction)
67+
s.run("NOT VALID").consume()
6268
except Exception as e:
6369
context.exception = e
6470

@@ -83,6 +89,7 @@ def step_impl(context):
8389

8490
@step("it throws a `ClientException`")
8591
def step_impl(context):
92+
print(context.exception)
8693
assert context.exception is not None
8794
assert type(context.exception) == ProtocolError or type(context.exception) == CypherError
8895
assert isinstance(context.exception, ProtocolError) or isinstance(context.exception, CypherError)

0 commit comments

Comments
 (0)