Skip to content

Commit c469ded

Browse files
committed
v0.20.0 added end-to-end tests using Testcontainers
1 parent c5b423a commit c469ded

File tree

17 files changed

+1920
-5
lines changed

17 files changed

+1920
-5
lines changed

.env.example

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,16 @@ N4JET_DEBUG=false
101101
# Default: true
102102
N4JET_VALIDATE_JSON=true
103103

104+
# ===============================================
105+
# Test Configuration
106+
# ===============================================
107+
108+
# Minimum log level for test output
109+
# Controls verbosity of test execution logs
110+
# Values: Debug, Info, Warn, Error, Fatal (case-insensitive)
111+
# Default: Info
112+
N4JET_TEST_LOG_LEVEL=Info
113+
104114
# ===============================================
105115
# Security Settings
106116
# ===============================================

.github/workflows/build.yml

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
name: Build and Test
2+
3+
on:
4+
push:
5+
branches: [ main, develop ]
6+
pull_request:
7+
branches: [ main ]
8+
9+
jobs:
10+
build:
11+
name: Build and Compile Check
12+
runs-on: ubuntu-latest
13+
14+
steps:
15+
- uses: actions/checkout@v3
16+
17+
- name: Setup .NET
18+
uses: actions/setup-dotnet@v3
19+
with:
20+
dotnet-version: '8.0.x'
21+
22+
- name: Restore dependencies
23+
run: dotnet restore
24+
25+
- name: Build
26+
run: dotnet build --no-restore
27+
28+
test-all:
29+
name: All Tests
30+
runs-on: ubuntu-latest
31+
needs: [build]
32+
if: github.event_name == 'push' || github.event.pull_request.draft == false
33+
34+
services:
35+
docker:
36+
image: docker:dind
37+
options: --privileged
38+
39+
steps:
40+
- uses: actions/checkout@v3
41+
42+
- name: Setup .NET
43+
uses: actions/setup-dotnet@v3
44+
with:
45+
dotnet-version: '8.0.x'
46+
47+
- name: Restore dependencies
48+
run: dotnet restore
49+
50+
- name: Build
51+
run: dotnet build --no-restore
52+
53+
- name: Run all tests including containers
54+
run: dotnet test --no-build --verbosity normal
55+
env:
56+
N4JET_TEST_LOG_LEVEL: Info
57+
58+
build-release:
59+
name: Build Release Binaries
60+
runs-on: ubuntu-latest
61+
needs: [build]
62+
63+
strategy:
64+
matrix:
65+
runtime: [win-x64, linux-x64, osx-x64, osx-arm64]
66+
67+
steps:
68+
- uses: actions/checkout@v3
69+
70+
- name: Setup .NET
71+
uses: actions/setup-dotnet@v3
72+
with:
73+
dotnet-version: '8.0.x'
74+
75+
- name: Publish ${{ matrix.runtime }}
76+
run: |
77+
dotnet publish Neo4jExport/Neo4jExport.fsproj \
78+
-c Release \
79+
-r ${{ matrix.runtime }} \
80+
--self-contained \
81+
-p:PublishSingleFile=true \
82+
-p:PublishTrimmed=true \
83+
-o ./publish/${{ matrix.runtime }}
84+
85+
- name: Upload artifact
86+
uses: actions/upload-artifact@v3
87+
with:
88+
name: neo4j-export-${{ matrix.runtime }}
89+
path: ./publish/${{ matrix.runtime }}/

.version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.19.0
1+
0.20.0

Directory.Build.props

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project>
22
<PropertyGroup>
3-
<Version>0.19.0</Version>
4-
<AssemblyVersion>0.19.0.0</AssemblyVersion>
5-
<FileVersion>0.19.0.0</FileVersion>
3+
<Version>0.20.0</Version>
4+
<AssemblyVersion>0.20.0.0</AssemblyVersion>
5+
<FileVersion>0.20.0.0</FileVersion>
66
</PropertyGroup>
77
</Project>
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
module Neo4jExport.Tests.EndToEnd.ExampleContainerTests
2+
3+
open Expecto
4+
open Swensen.Unquote
5+
open Neo4j.Driver
6+
open Neo4jExport.Tests.EndToEnd.Infrastructure.ContainerFixtures
7+
open Neo4jExport.Tests.Helpers.TestLog
8+
open Neo4jExport.Tests.EndToEnd.Infrastructure.TestDataManagement
9+
10+
[<Tests>]
11+
let tests =
12+
let testCount = 7 // Update this if adding/removing tests
13+
testListStart "Container Infrastructure Examples" testCount
14+
15+
// Tag container tests so they can be easily filtered
16+
testList
17+
"Container Infrastructure Examples"
18+
[
19+
20+
// Basic container test
21+
Fixtures.testWithCleanDb "can connect to Neo4j container" (fun driver ->
22+
async {
23+
use session = driver.AsyncSession()
24+
25+
let! cursor =
26+
session.RunAsync("RETURN 'Hello from Neo4j!' as message")
27+
|> Async.AwaitTask
28+
29+
let! record = cursor.SingleAsync() |> Async.AwaitTask
30+
31+
let message =
32+
record.["message"].As<string>()
33+
34+
test <@ message = "Hello from Neo4j!" @>
35+
})
36+
37+
// Multi-version test
38+
Fixtures.testWithAllVersions "verify version detection" (fun driver ->
39+
async {
40+
use session = driver.AsyncSession()
41+
42+
let! cursor =
43+
session.RunAsync(
44+
"CALL dbms.components() YIELD name, versions RETURN name, versions[0] as version"
45+
)
46+
|> Async.AwaitTask
47+
48+
let! records = cursor.ToListAsync() |> Async.AwaitTask
49+
50+
let neo4jRecord =
51+
records
52+
|> Seq.find (fun r -> r.["name"].As<string>() = "Neo4j Kernel")
53+
54+
let version =
55+
neo4jRecord.["version"].As<string>()
56+
57+
test
58+
<@
59+
version.StartsWith("4.4")
60+
|| version.StartsWith("5.")
61+
@>
62+
})
63+
64+
// Data seeding test
65+
Fixtures.testWithData "verify simple graph seeding" SimpleGraph Small (fun driver ->
66+
async {
67+
use session = driver.AsyncSession()
68+
69+
// Count nodes
70+
let! cursor =
71+
session.RunAsync("MATCH (n:TestNode) RETURN count(n) as count")
72+
|> Async.AwaitTask
73+
74+
let! record = cursor.SingleAsync() |> Async.AwaitTask
75+
let nodeCount = record.["count"].As<int64>()
76+
77+
// Count relationships
78+
let! cursor =
79+
session.RunAsync("MATCH ()-[r:CONNECTED]->() RETURN count(r) as count")
80+
|> Async.AwaitTask
81+
82+
let! record = cursor.SingleAsync() |> Async.AwaitTask
83+
let relCount = record.["count"].As<int64>()
84+
85+
test <@ nodeCount = 5000L @>
86+
test <@ relCount = 10000L @>
87+
})
88+
89+
// Complex types test
90+
Fixtures.testWithData "verify complex types seeding" ComplexTypes Small (fun driver ->
91+
async {
92+
use session = driver.AsyncSession()
93+
94+
// Check temporal node
95+
let! cursor =
96+
session.RunAsync("MATCH (n:TemporalNode) RETURN n")
97+
|> Async.AwaitTask
98+
99+
let! record = cursor.SingleAsync() |> Async.AwaitTask
100+
let node = record.["n"].As<INode>()
101+
102+
test <@ node.Properties.ContainsKey("date") @>
103+
test <@ node.Properties.ContainsKey("duration") @>
104+
test <@ node.Properties.ContainsKey("epochDateTime") @>
105+
test <@ node.Properties.ContainsKey("negativeDuration") @>
106+
107+
// Check spatial node
108+
let! cursor =
109+
session.RunAsync("MATCH (n:SpatialNode) RETURN n")
110+
|> Async.AwaitTask
111+
112+
let! record = cursor.SingleAsync() |> Async.AwaitTask
113+
let node = record.["n"].As<INode>()
114+
115+
test <@ node.Properties.ContainsKey("wgs84_2d") @>
116+
test <@ node.Properties.ContainsKey("cartesian3d") @>
117+
test <@ node.Properties.ContainsKey("highPrecision") @>
118+
test <@ node.Properties.ContainsKey("pointList2d") @>
119+
test <@ node.Properties.ContainsKey("pointListGeo") @>
120+
test <@ node.Properties.ContainsKey("emptyPointList") @>
121+
122+
// Check all primitive type nodes exist
123+
let primitiveLabels =
124+
[ "PrimitiveNode"
125+
"FloatNode"
126+
"StringNode"
127+
"CollectionNode"
128+
"SerializationTestNode"
129+
"MapDataNode"
130+
"ByteArrayNode"
131+
"LongStringNode" ]
132+
133+
for label in primitiveLabels do
134+
let! cursor =
135+
session.RunAsync($"MATCH (n:{label}) RETURN count(n) as cnt")
136+
|> Async.AwaitTask
137+
138+
let! record = cursor.SingleAsync() |> Async.AwaitTask
139+
let count = record.["cnt"].As<int64>()
140+
test <@ count > 0L @>
141+
})
142+
143+
// Edge cases test
144+
Fixtures.testWithData "verify edge cases seeding" EdgeCases Small (fun driver ->
145+
async {
146+
use session = driver.AsyncSession()
147+
148+
// Check boundary node
149+
let! cursor =
150+
session.RunAsync("MATCH (n:BoundaryNode) RETURN n")
151+
|> Async.AwaitTask
152+
153+
let! record = cursor.SingleAsync() |> Async.AwaitTask
154+
let node = record.["n"].As<INode>()
155+
156+
test <@ node.Properties.ContainsKey("maxInt") @>
157+
test <@ node.Properties.ContainsKey("кириллица") @> // Cyrillic property
158+
test <@ node.Properties.ContainsKey("中文属性") @> // Chinese property
159+
160+
// Check self-referencing node
161+
let! cursor =
162+
session.RunAsync("""MATCH (n:SelfRefNode)-[r:SELF_REF]->(n) RETURN count(r) as selfRefs""")
163+
|> Async.AwaitTask
164+
165+
let! record = cursor.SingleAsync() |> Async.AwaitTask
166+
167+
let selfRefs =
168+
record.["selfRefs"].As<int64>()
169+
170+
test <@ selfRefs = 1L @>
171+
})
172+
173+
// Truncation tests
174+
Fixtures.testWithData "verify truncation test data" TruncationTests Small (fun driver ->
175+
async {
176+
use session = driver.AsyncSession()
177+
178+
// Check node with exactly 10 labels
179+
let! cursor =
180+
session.RunAsync(
181+
"""MATCH (n:L1:L2:L3:L4:L5:L6:L7:L8:L9:L10 {id: 'exactly-10-labels'}) RETURN size(labels(n)) as labelCount"""
182+
)
183+
|> Async.AwaitTask
184+
185+
let! record = cursor.SingleAsync() |> Async.AwaitTask
186+
187+
let labelCount =
188+
record.["labelCount"].As<int64>()
189+
190+
test <@ labelCount = 10L @>
191+
192+
// Check truncation boundary node exists
193+
let! cursor =
194+
session.RunAsync("MATCH (n:TruncationBoundary) RETURN n")
195+
|> Async.AwaitTask
196+
197+
let! record = cursor.SingleAsync() |> Async.AwaitTask
198+
let node = record.["n"].As<INode>()
199+
200+
test <@ node.Properties.ContainsKey("exactLimit") @>
201+
test <@ node.Properties.ContainsKey("overLimit") @>
202+
}) ]

0 commit comments

Comments
 (0)