mirror of
https://github.com/awatertrevi/xamarin-neo4j.git
synced 2026-09-22 09:05:29 +00:00
integration tests
This commit is contained in:
@@ -8,6 +8,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.Neo4j.iOS", "Xamari
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.Neo4j.Tests", "Xamarin.Neo4j\Xamarin.Neo4j.Tests\Xamarin.Neo4j.Tests.csproj", "{A9874913-14B2-4BED-A838-89697258405A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.Neo4j.IntegrationTests", "Xamarin.Neo4j\Xamarin.Neo4j.IntegrationTests\Xamarin.Neo4j.IntegrationTests.csproj", "{B3E7D2A1-5C4F-4A8B-9D6E-1F2A3B4C5D6E}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Ad-Hoc|iPhone = Ad-Hoc|iPhone
|
||||
@@ -100,5 +102,21 @@ Global
|
||||
{A9874913-14B2-4BED-A838-89697258405A}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{A9874913-14B2-4BED-A838-89697258405A}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{A9874913-14B2-4BED-A838-89697258405A}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{B3E7D2A1-5C4F-4A8B-9D6E-1F2A3B4C5D6E}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{B3E7D2A1-5C4F-4A8B-9D6E-1F2A3B4C5D6E}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
|
||||
{B3E7D2A1-5C4F-4A8B-9D6E-1F2A3B4C5D6E}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{B3E7D2A1-5C4F-4A8B-9D6E-1F2A3B4C5D6E}.AppStore|iPhone.Build.0 = Debug|Any CPU
|
||||
{B3E7D2A1-5C4F-4A8B-9D6E-1F2A3B4C5D6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B3E7D2A1-5C4F-4A8B-9D6E-1F2A3B4C5D6E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B3E7D2A1-5C4F-4A8B-9D6E-1F2A3B4C5D6E}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{B3E7D2A1-5C4F-4A8B-9D6E-1F2A3B4C5D6E}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||
{B3E7D2A1-5C4F-4A8B-9D6E-1F2A3B4C5D6E}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{B3E7D2A1-5C4F-4A8B-9D6E-1F2A3B4C5D6E}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{B3E7D2A1-5C4F-4A8B-9D6E-1F2A3B4C5D6E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B3E7D2A1-5C4F-4A8B-9D6E-1F2A3B4C5D6E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B3E7D2A1-5C4F-4A8B-9D6E-1F2A3B4C5D6E}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{B3E7D2A1-5C4F-4A8B-9D6E-1F2A3B4C5D6E}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{B3E7D2A1-5C4F-4A8B-9D6E-1F2A3B4C5D6E}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{B3E7D2A1-5C4F-4A8B-9D6E-1F2A3B4C5D6E}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using DotNet.Testcontainers.Builders;
|
||||
using DotNet.Testcontainers.Containers;
|
||||
using Neo4j.Driver;
|
||||
|
||||
namespace Xamarin.Neo4j.IntegrationTests.Fixtures
|
||||
{
|
||||
public class MemgraphFixture : IAsyncDisposable
|
||||
{
|
||||
private IContainer _container;
|
||||
|
||||
public string BoltUri { get; private set; }
|
||||
|
||||
public async Task StartAsync()
|
||||
{
|
||||
_container = new ContainerBuilder()
|
||||
.WithImage("memgraph/memgraph:latest")
|
||||
.WithPortBinding(7687, true)
|
||||
.WithCommand("--bolt-server-name-for-init=Neo4j/5.0.0", "--also-log-to-stderr")
|
||||
.WithWaitStrategy(Wait.ForUnixContainer()
|
||||
.UntilPortIsAvailable(7687))
|
||||
.Build();
|
||||
|
||||
await _container.StartAsync();
|
||||
|
||||
var boltPort = _container.GetMappedPublicPort(7687);
|
||||
BoltUri = $"bolt://localhost:{boltPort}";
|
||||
|
||||
Console.Error.WriteLine($"[MemgraphFixture] Container started, BoltUri={BoltUri}");
|
||||
|
||||
await WaitForReady();
|
||||
|
||||
Console.Error.WriteLine("[MemgraphFixture] Ready!");
|
||||
}
|
||||
|
||||
private async Task WaitForReady()
|
||||
{
|
||||
for (var i = 0; i < 30; i++)
|
||||
{
|
||||
IDriver driver = null;
|
||||
try
|
||||
{
|
||||
driver = GraphDatabase.Driver(BoltUri);
|
||||
await using var session = driver.AsyncSession();
|
||||
var cursor = await session.RunAsync("RETURN 1 AS n");
|
||||
await cursor.ConsumeAsync();
|
||||
return;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var inner = ex.InnerException != null ? $" -> {ex.InnerException.GetType().Name}: {ex.InnerException.Message}" : "";
|
||||
Console.Error.WriteLine($"[Memgraph] Attempt {i}: {ex.GetType().Name}: {ex.Message}{inner}");
|
||||
await Task.Delay(2000);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (driver != null) await driver.DisposeAsync();
|
||||
}
|
||||
}
|
||||
|
||||
throw new TimeoutException("Memgraph container did not become ready in time.");
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_container != null)
|
||||
await _container.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using DotNet.Testcontainers.Builders;
|
||||
using DotNet.Testcontainers.Containers;
|
||||
using Neo4j.Driver;
|
||||
|
||||
namespace Xamarin.Neo4j.IntegrationTests.Fixtures
|
||||
{
|
||||
public class Neo4jFixture : IAsyncDisposable
|
||||
{
|
||||
private IContainer _container;
|
||||
|
||||
public string BoltUri { get; private set; }
|
||||
public string Username => "neo4j";
|
||||
public string Password => "testpassword";
|
||||
|
||||
public async Task StartAsync()
|
||||
{
|
||||
_container = new ContainerBuilder()
|
||||
.WithImage("neo4j:latest")
|
||||
.WithPortBinding(7474, true)
|
||||
.WithPortBinding(7687, true)
|
||||
.WithEnvironment("NEO4J_AUTH", $"{Username}/{Password}")
|
||||
.WithWaitStrategy(Wait.ForUnixContainer()
|
||||
.UntilPortIsAvailable(7687))
|
||||
.Build();
|
||||
|
||||
await _container.StartAsync();
|
||||
|
||||
var boltPort = _container.GetMappedPublicPort(7687);
|
||||
BoltUri = $"bolt://localhost:{boltPort}";
|
||||
|
||||
await WaitForReady();
|
||||
}
|
||||
|
||||
private async Task WaitForReady()
|
||||
{
|
||||
var driver = GraphDatabase.Driver(BoltUri, AuthTokens.Basic(Username, Password));
|
||||
|
||||
for (var i = 0; i < 30; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
await driver.VerifyConnectivityAsync();
|
||||
await driver.DisposeAsync();
|
||||
return;
|
||||
}
|
||||
catch
|
||||
{
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
}
|
||||
|
||||
await driver.DisposeAsync();
|
||||
throw new TimeoutException("Neo4j container did not become ready in time.");
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_container != null)
|
||||
await _container.DisposeAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
using System.Threading.Tasks;
|
||||
using Neo4j.Driver;
|
||||
using NUnit.Framework;
|
||||
using Xamarin.Neo4j.IntegrationTests.Fixtures;
|
||||
|
||||
namespace Xamarin.Neo4j.IntegrationTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class MemgraphConnectivityTests
|
||||
{
|
||||
private static MemgraphFixture _fixture;
|
||||
private IDriver _driver;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public async Task OneTimeSetUp()
|
||||
{
|
||||
_fixture = new MemgraphFixture();
|
||||
await _fixture.StartAsync();
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
public async Task OneTimeTearDown()
|
||||
{
|
||||
if (_fixture != null)
|
||||
await _fixture.DisposeAsync();
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
_driver = GraphDatabase.Driver(_fixture.BoltUri);
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public async Task TearDown()
|
||||
{
|
||||
if (_driver != null)
|
||||
await _driver.DisposeAsync();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CanEstablishConnection()
|
||||
{
|
||||
await using var session = _driver.AsyncSession();
|
||||
var result = await session.RunAsync("RETURN 1 AS n");
|
||||
var record = await result.SingleAsync();
|
||||
Assert.AreEqual(1L, record["n"].As<long>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CanExecuteCypherQuery()
|
||||
{
|
||||
await using var session = _driver.AsyncSession();
|
||||
|
||||
var result = await session.RunAsync("CREATE (n:Test {name: 'hello'}) RETURN n.name AS name");
|
||||
var record = await result.SingleAsync();
|
||||
|
||||
Assert.AreEqual("hello", record["name"].As<string>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CanQueryNodesAndRelationships()
|
||||
{
|
||||
await using var session = _driver.AsyncSession();
|
||||
|
||||
await session.RunAsync(
|
||||
"CREATE (a:Person {name: 'Alice'})-[:KNOWS]->(b:Person {name: 'Bob'})");
|
||||
|
||||
var cursor = await session.RunAsync(
|
||||
"MATCH (a:Person)-[r:KNOWS]->(b:Person) RETURN a.name AS from, b.name AS to, type(r) AS rel");
|
||||
var record = await cursor.SingleAsync();
|
||||
|
||||
Assert.AreEqual("Alice", record["from"].As<string>());
|
||||
Assert.AreEqual("Bob", record["to"].As<string>());
|
||||
Assert.AreEqual("KNOWS", record["rel"].As<string>());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
using System.Threading.Tasks;
|
||||
using Neo4j.Driver;
|
||||
using NUnit.Framework;
|
||||
using Xamarin.Neo4j.IntegrationTests.Fixtures;
|
||||
|
||||
namespace Xamarin.Neo4j.IntegrationTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class Neo4jConnectivityTests
|
||||
{
|
||||
private static Neo4jFixture _fixture;
|
||||
private IDriver _driver;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public async Task OneTimeSetUp()
|
||||
{
|
||||
_fixture = new Neo4jFixture();
|
||||
await _fixture.StartAsync();
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
public async Task OneTimeTearDown()
|
||||
{
|
||||
if (_fixture != null)
|
||||
await _fixture.DisposeAsync();
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
_driver = GraphDatabase.Driver(
|
||||
_fixture.BoltUri,
|
||||
AuthTokens.Basic(_fixture.Username, _fixture.Password));
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public async Task TearDown()
|
||||
{
|
||||
if (_driver != null)
|
||||
await _driver.DisposeAsync();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CanEstablishConnection()
|
||||
{
|
||||
await _driver.VerifyConnectivityAsync();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CanExecuteCypherQuery()
|
||||
{
|
||||
await using var session = _driver.AsyncSession();
|
||||
|
||||
var result = await session.RunAsync("CREATE (n:Test {name: 'hello'}) RETURN n.name AS name");
|
||||
var record = await result.SingleAsync();
|
||||
|
||||
Assert.AreEqual("hello", record["name"].As<string>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CanQueryNodesAndRelationships()
|
||||
{
|
||||
await using var session = _driver.AsyncSession();
|
||||
|
||||
await session.RunAsync(
|
||||
"CREATE (a:Person {name: 'Alice'})-[:KNOWS]->(b:Person {name: 'Bob'})");
|
||||
|
||||
var cursor = await session.RunAsync(
|
||||
"MATCH (a:Person)-[r:KNOWS]->(b:Person) RETURN a.name AS from, b.name AS to, type(r) AS rel");
|
||||
var record = await cursor.SingleAsync();
|
||||
|
||||
Assert.AreEqual("Alice", record["from"].As<string>());
|
||||
Assert.AreEqual("Bob", record["to"].As<string>());
|
||||
Assert.AreEqual("KNOWS", record["rel"].As<string>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CanExpandNode()
|
||||
{
|
||||
await using var session = _driver.AsyncSession();
|
||||
|
||||
var setupResult = await session.RunAsync(
|
||||
"CREATE (c:Hub {name: 'Center'})" +
|
||||
"CREATE (c)-[:LINK]->(a:Leaf {name: 'A'})" +
|
||||
"CREATE (c)-[:LINK]->(b:Leaf {name: 'B'})" +
|
||||
"CREATE (c)-[:LINK]->(d:Leaf {name: 'C'})" +
|
||||
"RETURN id(c) AS centerId");
|
||||
var setupRecord = await setupResult.SingleAsync();
|
||||
var centerId = setupRecord["centerId"].As<long>();
|
||||
|
||||
var cursor = await session.RunAsync(
|
||||
"MATCH (n)-[r]-(m) WHERE id(n) = $nodeId RETURN n, r, m",
|
||||
new { nodeId = centerId });
|
||||
|
||||
var records = await cursor.ToListAsync();
|
||||
|
||||
Assert.AreEqual(3, records.Count);
|
||||
|
||||
foreach (var record in records)
|
||||
{
|
||||
Assert.IsInstanceOf<INode>(record["n"]);
|
||||
Assert.IsInstanceOf<IRelationship>(record["r"]);
|
||||
Assert.IsInstanceOf<INode>(record["m"]);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CanParseConnectionString()
|
||||
{
|
||||
var connectionString = new Models.Neo4jConnectionString
|
||||
{
|
||||
Scheme = "bolt://",
|
||||
Host = "localhost:7687",
|
||||
Username = _fixture.Username,
|
||||
Password = _fixture.Password,
|
||||
Database = "neo4j"
|
||||
};
|
||||
|
||||
var result = connectionString.ParseHost();
|
||||
|
||||
Assert.AreEqual("bolt://localhost:7687", result.Item1);
|
||||
Assert.IsFalse(result.Item2);
|
||||
Assert.IsFalse(result.Item3);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
|
||||
<PackageReference Include="Testcontainers" Version="4.3.0" />
|
||||
<PackageReference Include="Neo4j.Driver" Version="5.28.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Xamarin.Neo4j\Models\Neo4jConnectionString.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user