Skip to content

Commit

Permalink
Merge pull request #116 from cskardon/Issue-115
Browse files Browse the repository at this point in the history
Add ability to set the Planner
  • Loading branch information
cskardon committed Sep 2, 2015
2 parents aab64e4 + 619ca55 commit 0113b28
Show file tree
Hide file tree
Showing 18 changed files with 210 additions and 74 deletions.
54 changes: 54 additions & 0 deletions Neo4jClient.Tests/Cypher/CypherFluentQueryPlannerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System;
using Neo4jClient.Cypher;
using NSubstitute;
using NUnit.Framework;

namespace Neo4jClient.Test.Cypher
{
/// <summary>
/// Tests for the UNWIND operator
/// </summary>
[TestFixture]
public class CypherFluentQueryPlannerTests
{
[Test]
public void TestPlannerWithFreeTextConstruction()
{
var client = Substitute.For<IRawGraphClient>();
client.CypherCapabilities.Returns(CypherCapabilities.Cypher22);

var query = new CypherFluentQuery(client)
.Planner("FreePlanner")
.Query;

Assert.AreEqual("PLANNER FreePlanner", query.QueryText);
}

[Test, Sequential]
public void TestPlannerWithCypherPlannerVariant(
[Values(CypherPlanner.CostGreedy,CypherPlanner.CostIdp, CypherPlanner.Rule)] CypherPlanner input,
[Values("COST", "IDP", "RULE")] string expected)
{
var client = Substitute.For<IRawGraphClient>();
client.CypherCapabilities.Returns(CypherCapabilities.Cypher22);

var query = new CypherFluentQuery(client)
.Planner(input)
.Query;

Assert.AreEqual(string.Format("PLANNER {0}", expected), query.QueryText);
}

[Test]
[ExpectedException(typeof (InvalidOperationException))]
public void ThrowsInvalidOperationException_WhenNeo4jVersionIsLessThan22()
{
var client = Substitute.For<IRawGraphClient>();
client.CypherCapabilities.Returns(CypherCapabilities.Cypher19);

var _ = new CypherFluentQuery(client)
.Planner("FreePlanner")
.Query;
}
}
}
10 changes: 10 additions & 0 deletions Neo4jClient.Tests/GraphClientTests/ConnectTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,16 @@ public void ShouldReturnCypher19CapabilitiesForPre20Version()
}
}

[Test]
public void ShouldSetCypher22CapabilitiesForPost22Version()
{
using (var testHarness = new RestTestHarness())
{
var graphClient = testHarness.CreateAndConnectGraphClient(RestTestHarness.Neo4jVersion.Neo22);
Assert.AreEqual(CypherCapabilities.Cypher22, graphClient.CypherCapabilities);
}
}

[Test]
public void ShouldReturnCypher19CapabilitiesForVersion20()
{
Expand Down
16 changes: 16 additions & 0 deletions Neo4jClient.Tests/MockResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,22 @@ public static MockResponse NeoRoot20()
}");
}

public static MockResponse NeoRoot22()
{
return Json(HttpStatusCode.OK, @"{
'cypher' : 'http://foo/db/data/cypher',
'batch' : 'http://foo/db/data/batch',
'node' : 'http://foo/db/data/node',
'node_index' : 'http://foo/db/data/index/node',
'relationship_index' : 'http://foo/db/data/index/relationship',
'reference_node' : 'http://foo/db/data/node/123',
'neo4j_version' : '2.2.1',
'transaction': 'http://foo/db/data/transaction',
'extensions_info' : 'http://foo/db/data/ext',
'extensions' : {}
}");
}

public static MockResponse NeoRootPre15M02()
{
return Json(HttpStatusCode.OK, @"{
Expand Down
1 change: 1 addition & 0 deletions Neo4jClient.Tests/Neo4jClient.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
<Compile Include="Cypher\CypherFluentQueryMergeTests.cs" />
<Compile Include="Cypher\CypherFluentQueryMatchTests.cs" />
<Compile Include="Cypher\CypherFluentQueryRemoveTests.cs" />
<Compile Include="Cypher\CypherFluentQueryPlannerTests.cs" />
<Compile Include="Cypher\CypherFluentQueryUnwindTests.cs" />
<Compile Include="Cypher\CypherFluentQueryUsingIndexTests.cs" />
<Compile Include="Cypher\CypherFluentQueryWithParamTests.cs" />
Expand Down
2 changes: 2 additions & 0 deletions Neo4jClient.Tests/Neo4jClient.Tests.csproj.DotSettings
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/CSharpLanguageProject/LanguageLevel/@EntryValue">CSharp50</s:String></wpf:ResourceDictionary>
107 changes: 53 additions & 54 deletions Neo4jClient.Tests/RestTestHarness.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ namespace Neo4jClient.Test
{
public class RestTestHarness : IEnumerable, IDisposable
{
public enum Neo4jVersion
{
Neo19,
Neo20,
Neo22
}

readonly IDictionary<MockRequest, MockResponse> recordedResponses = new Dictionary<MockRequest, MockResponse>();
readonly List<MockRequest> requestsThatShouldNotBeProcessed = new List<MockRequest>();
readonly IList<MockRequest> processedRequests = new List<MockRequest>();
Expand Down Expand Up @@ -43,10 +50,27 @@ public RestTestHarness ShouldNotBeCalled(params MockRequest[] requests)
return this;
}

public GraphClient CreateGraphClient(bool neo4j2)
public GraphClient CreateGraphClient(Neo4jVersion neoVersion)
{
if (!recordedResponses.Keys.Any(r => r.Resource == "" || r.Resource == "/"))
Add(MockRequest.Get(""), neo4j2 ? MockResponse.NeoRoot20() : MockResponse.NeoRoot());
{
MockResponse response;
switch (neoVersion)
{
case Neo4jVersion.Neo19:
response = MockResponse.NeoRoot();
break;
case Neo4jVersion.Neo20:
response = MockResponse.NeoRoot20();
break;
case Neo4jVersion.Neo22:
response = MockResponse.NeoRoot22();
break;
default:
throw new ArgumentOutOfRangeException("neoVersion", neoVersion, null);
}
Add(MockRequest.Get(""), response);
}

var httpClient = GenerateHttpClient(BaseUri);

Expand All @@ -56,14 +80,14 @@ public GraphClient CreateGraphClient(bool neo4j2)

public ITransactionalGraphClient CreateAndConnectTransactionalGraphClient()
{
var graphClient = CreateGraphClient(true);
var graphClient = CreateGraphClient(Neo4jVersion.Neo20);
graphClient.Connect();
return graphClient;
}

public IRawGraphClient CreateAndConnectGraphClient()
public IRawGraphClient CreateAndConnectGraphClient(Neo4jVersion version = Neo4jVersion.Neo19)
{
var graphClient = CreateGraphClient(false);
var graphClient = CreateGraphClient(version);
graphClient.Connect();
return graphClient;
}
Expand All @@ -78,45 +102,32 @@ public void AssertRequestConstraintsAreMet()
if (unservicedRequests.Any())
Assert.Fail(string.Join("\r\n\r\n", unservicedRequests.ToArray()));

var resourcesThatWereNeverRequested = recordedResponses
.Select(r => r.Key)
.Where(r => !(processedRequests.Contains(r) || requestsThatShouldNotBeProcessed.Contains(r)))
.Select(r => string.Format("{0} {1}", r.Method, r.Resource))
.ToArray();
var resourcesThatWereNeverRequested = recordedResponses.Select(r => r.Key).Where(r => !(processedRequests.Contains(r) || requestsThatShouldNotBeProcessed.Contains(r))).Select(r => string.Format("{0} {1}", r.Method, r.Resource)).ToArray();

var processedResourcesThatShouldntHaveBeenRequested = requestsThatShouldNotBeProcessed
.Where(r => processedRequests.Contains(r))
.Select(r => string.Format("{0} {1}", r.Method, r.Resource))
.ToArray();
var processedResourcesThatShouldntHaveBeenRequested = requestsThatShouldNotBeProcessed.Where(r => processedRequests.Contains(r)).Select(r => string.Format("{0} {1}", r.Method, r.Resource)).ToArray();

if (processedResourcesThatShouldntHaveBeenRequested.Any())
{
Assert.Fail(
"The test should not have made REST requests for the following resources: {0}",
string.Join(", ", processedResourcesThatShouldntHaveBeenRequested));
Assert.Fail("The test should not have made REST requests for the following resources: {0}", string.Join(", ", processedResourcesThatShouldntHaveBeenRequested));
}

if (!resourcesThatWereNeverRequested.Any())
return;

Assert.Fail(
"The test expected REST requests for the following resources, but they were never made: {0}",
string.Join(", ", resourcesThatWereNeverRequested));
Assert.Fail("The test expected REST requests for the following resources, but they were never made: {0}", string.Join(", ", resourcesThatWereNeverRequested));
}

public IHttpClient GenerateHttpClient(string baseUri)
{
var httpClient = Substitute.For<IHttpClient>();

httpClient
.SendAsync(Arg.Any<HttpRequestMessage>())
.ReturnsForAnyArgs(ci =>
{
var request = ci.Arg<HttpRequestMessage>();
var task = new Task<HttpResponseMessage>(() => HandleRequest(request, baseUri));
task.Start();
return task;
});
httpClient.SendAsync(Arg.Any<HttpRequestMessage>()).ReturnsForAnyArgs(ci =>
{
var request = ci.Arg<HttpRequestMessage>();
var task = new Task<HttpResponseMessage>(() => HandleRequest(request, baseUri));
task.Start();
return task;
});

return httpClient;
}
Expand All @@ -128,9 +139,7 @@ protected virtual HttpResponseMessage HandleRequest(HttpRequestMessage request,
if (!string.IsNullOrEmpty(requestUri.UserInfo))
requestUri = new UriBuilder(requestUri) {UserName = "", Password = ""}.Uri;

var matchingRequests = recordedResponses
.Where(can => requestUri.AbsoluteUri == baseUri + can.Key.Resource)
.Where(can => request.Method.ToString().Equals(can.Key.Method.ToString(), StringComparison.OrdinalIgnoreCase));
var matchingRequests = recordedResponses.Where(can => requestUri.AbsoluteUri == baseUri + can.Key.Resource).Where(can => request.Method.ToString().Equals(can.Key.Method.ToString(), StringComparison.OrdinalIgnoreCase));

string requestBody = null;
if (request.Content != null)
Expand All @@ -142,14 +151,13 @@ protected virtual HttpResponseMessage HandleRequest(HttpRequestMessage request,

if (request.Method == HttpMethod.Post)
{
matchingRequests = matchingRequests
.Where(can =>
{
var cannedRequest = can.Key;
var cannedRequestBody = cannedRequest.Body;
cannedRequestBody = cannedRequestBody ?? "";
return IsJsonEquivalent(cannedRequestBody, requestBody);
});
matchingRequests = matchingRequests.Where(can =>
{
var cannedRequest = can.Key;
var cannedRequestBody = cannedRequest.Body;
cannedRequestBody = cannedRequestBody ?? "";
return IsJsonEquivalent(cannedRequestBody, requestBody);
});
}

var results = matchingRequests.ToArray();
Expand All @@ -173,9 +181,7 @@ protected virtual HttpResponseMessage HandleRequest(HttpRequestMessage request,

var httpResponse = new HttpResponseMessage
{
StatusCode = response.StatusCode,
ReasonPhrase = response.StatusDescription,
Content = string.IsNullOrEmpty(response.Content) ? null : new StringContent(response.Content, null, response.ContentType)
StatusCode = response.StatusCode, ReasonPhrase = response.StatusDescription, Content = string.IsNullOrEmpty(response.Content) ? null : new StringContent(response.Content, null, response.ContentType)
};

if (string.IsNullOrEmpty(response.Location))
Expand All @@ -187,26 +193,19 @@ protected virtual HttpResponseMessage HandleRequest(HttpRequestMessage request,
return httpResponse;
}

static bool IsJsonEquivalent(string lhs, string rhs)
private static bool IsJsonEquivalent(string lhs, string rhs)
{
lhs = NormalizeJson(lhs);
rhs = NormalizeJson(rhs);
return lhs == rhs;
}

static string NormalizeJson(string input)
private static string NormalizeJson(string input)
{
if (input.First() == '"' &&
input.Last() == '"')
if (input.First() == '"' && input.Last() == '"')
input = input.Substring(1, input.Length - 2);

return input
.Replace(" ", "")
.Replace("'", "\"")
.Replace("\r", "")
.Replace("\\r", "")
.Replace("\n", "")
.Replace("\\n", "");
return input.Replace(" ", "").Replace("'", "\"").Replace("\r", "").Replace("\\r", "").Replace("\n", "").Replace("\\n", "");
}

public void Dispose()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public void BeginTransactionShouldFailWithLower20Versions()
{
using (var testHarness = new RestTestHarness())
{
var client = testHarness.CreateGraphClient(false);
var client = testHarness.CreateGraphClient(RestTestHarness.Neo4jVersion.Neo19);
client.Connect();
client.BeginTransaction();
}
Expand Down
15 changes: 13 additions & 2 deletions Neo4jClient/Cypher/CypherCapabilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,28 @@ public class CypherCapabilities
public readonly static CypherCapabilities Cypher19 = new CypherCapabilities
{
SupportsPropertySuffixesForControllingNullComparisons = true,
SupportsNullComparisonsWithIsOperator = true
SupportsNullComparisonsWithIsOperator = true,
SupportsPlanner = false
};

public readonly static CypherCapabilities Cypher20 = new CypherCapabilities
{
SupportsPropertySuffixesForControllingNullComparisons = false,
SupportsNullComparisonsWithIsOperator = false
SupportsNullComparisonsWithIsOperator = false,
SupportsPlanner = false
};


public static readonly CypherCapabilities Cypher22 = new CypherCapabilities
{
SupportsPlanner = true,
SupportsPropertySuffixesForControllingNullComparisons = false,
SupportsNullComparisonsWithIsOperator = false,
};

public static readonly CypherCapabilities Default = Cypher20;

public bool SupportsPlanner { get; set; }
public bool SupportsPropertySuffixesForControllingNullComparisons { get; set; }
public bool SupportsNullComparisonsWithIsOperator { get; set; }
}
Expand Down
Loading

0 comments on commit 0113b28

Please sign in to comment.