Skip to content

test(memgraph): run the shared suite against Memgraph via a memgraph: false tag #1865

test(memgraph): run the shared suite against Memgraph via a memgraph: false tag

test(memgraph): run the shared suite against Memgraph via a memgraph: false tag #1865

Workflow file for this run

name: Driver specs
on:
push:
branches: [main]
# Housekeeping-only changes (docs, changelog, version bump) can't affect
# tests — skip the heavy CI on them. A mixed change still runs (paths-ignore
# skips only when EVERY changed file matches).
paths-ignore: ['**/*.md', 'lib/shared/neo4j/driver/version.rb']
pull_request:
paths-ignore: ['**/*.md', 'lib/shared/neo4j/driver/version.rb']
jobs:
rspec:
name: rspec (ruby ${{ matrix.ruby }}${{ matrix.force_mri && ' [mri flavor]' || '' }} / neo4j ${{ matrix.neo4j }})
runs-on: ubuntu-latest
# JRuby rows are continue-on-error for now: native JRuby flavor has
# no code yet (lib/jruby/ empty), and the MRI-on-JRuby include row
# below is exercised here for the first time.
continue-on-error: ${{ startsWith(matrix.ruby, 'jruby') }}
strategy:
fail-fast: false
matrix:
ruby:
- "3.4"
- "4.0"
- "jruby-10.1.0.0"
neo4j:
- "4.4.48"
- "5.26.28"
- "2026.06.0"
# Default value so the `include` below adds a new row rather
# than merging into existing combinations (GH Actions semantics).
force_mri: [false]
include:
# MRI flavor on JRuby — exercises the cross-flavor path via
# NEO4J_DRIVER_FORCE_MRI=1 (the Gemfile's `gemspec name:` flip).
# Single Neo4j version to keep CI minutes reasonable.
- ruby: "jruby-10.1.0.0"
neo4j: "2026.06.0"
force_mri: true
services:
neo4j:
image: neo4j:${{ matrix.neo4j }}-enterprise
env:
NEO4J_AUTH: neo4j/password
NEO4J_ACCEPT_LICENSE_AGREEMENT: "yes"
# Allow the LoadCsv spec's `file://` import. The default import dir
# (/var/lib/neo4j/import) is the same across the 4.4/5.x/2026 matrix,
# so we mount a shared host dir there instead of reconfiguring the
# import path (whose config key differs between 4.4 and 5.x).
NEO4J_dbms_security_allow__csv__import__from__file__urls: "true"
ports:
- 7687:7687
- 7474:7474
# The host path is created by Docker at service start (before checkout);
# the "Prepare import dir" step below makes it writable by the runner.
options: >-
--health-cmd "cypher-shell -u neo4j -p password 'RETURN 1'"
--health-interval 10s
--health-timeout 5s
--health-retries 20
--health-start-period 30s
--volume /tmp/neo4j-import:/var/lib/neo4j/import
env:
TEST_NEO4J_URL: bolt://localhost:7687
TEST_NEO4J_USER: neo4j
TEST_NEO4J_PASS: password
# Shared host dir mounted into the neo4j service's import directory; the
# LoadCsv spec writes its file here and the server reads it via file://.
# Its presence is also what un-skips the csv-tagged example (see spec_helper).
TEST_NEO4J_IMPORT_DIR: /tmp/neo4j-import
# Empty string when force_mri is false; '1' on the include row.
# The Gemfile checks this and pins to the MRI gemspec when set.
NEO4J_DRIVER_FORCE_MRI: ${{ matrix.force_mri && '1' || '' }}
# Serialise gem installs. setup-ruby's `bundler-cache: true` runs
# `bundle install --jobs 4` by default; on JRuby the parallel
# workers race on the jar-dependencies post_install hook for the
# path-source gem (the hook calls back into ruby-maven before
# ruby-maven has finished extracting deps.lst / polyglot.jar).
# No-op on MRI (no hook); a few seconds slower than parallel but
# correct everywhere — matches testkit/build.py for the same
# reason. Cf. PR #303 commit 670dec3 fix in testkit/build.py.
BUNDLE_JOBS: '1'
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
bundler-cache: true
# The bind-mounted import dir is root-owned (and the neo4j entrypoint may
# chown it to the in-container neo4j user); make it writable by the runner
# so the LoadCsv spec can drop its file there. The spec chmods the file
# itself world-readable so the neo4j process can read it back.
- name: Prepare Neo4j import dir (LOAD CSV file:// tests)
run: sudo mkdir -p /tmp/neo4j-import && sudo chmod 1777 /tmp/neo4j-import
- name: Run RSpec
run: bundle exec rspec --format progress
# Memgraph compatibility: the SAME shared suite, run against Memgraph instead
# of Neo4j. Neo4j-only specs are tagged `memgraph: false` and skipped via
# TEST_MEMGRAPH (see spec_helper); everything else runs unchanged. Runs on both
# flavors — latest CRuby (pure-Ruby Bolt) and latest JRuby (Java driver, which
# Memgraph officially supports). See docs/memgraph.md.
rspec-memgraph:
name: rspec (memgraph / ruby ${{ matrix.ruby }}${{ matrix.force_mri && ' [mri flavor]' || '' }})
runs-on: ubuntu-latest
# Only the CRuby leg gates. Both JRuby legs (Java-driver flavor, and the
# MRI-flavor-on-JVM cross-flavor row) are non-blocking, matching the repo-wide
# JRuby policy. The mri-flavor-on-JVM row isolates whether a JRuby-only red is
# the Java driver's behavior (MRI-on-JVM passes) or a JVM/Linux environment
# effect (MRI-on-JVM fails too) — the same three-cell layout as the Neo4j job.
continue-on-error: ${{ startsWith(matrix.ruby, 'jruby') }}
strategy:
fail-fast: false
matrix:
ruby:
- "4.0" # latest CRuby — MRI pure-Ruby Bolt flavor
- "jruby-10.1.0.0" # latest JRuby — Java-driver flavor
force_mri: [false]
include:
# MRI flavor on JRuby — same env/platform as the Java-driver row but the
# pure-Ruby impl, via NEO4J_DRIVER_FORCE_MRI=1 (the Gemfile's flip).
- ruby: "jruby-10.1.0.0"
force_mri: true
services:
memgraph:
image: memgraph/memgraph:3.12.0 # pinned to the validated version
ports:
- 7687:7687
# MEMGRAPH_USER/PASSWORD create the user at startup, which turns auth on
# (Memgraph has no auth flag — the first user is the switch). The suite
# then authenticates with basic neo4j/password (the driver_helper default),
# exercising the auth path the same way as against Neo4j. Memgraph has no
# built-in Docker healthcheck; the query-ready step below waits.
env:
MEMGRAPH_USER: neo4j
MEMGRAPH_PASSWORD: password
env:
TEST_NEO4J_URL: bolt://localhost:7687
TEST_MEMGRAPH: "1" # activates the `memgraph: false` exclusion
TEST_NEO4J_DATABASE: memgraph # Memgraph's default database (no `neo4j` db)
# Memgraph advertises Neo4j/v5.11.0 compatibility; feeds version-gated specs.
NEO4J_VERSION: 5.11.0
# '1' on the mri-flavor-on-JVM row; empty otherwise. The Gemfile pins to the
# MRI gemspec when set (same as the rspec job's cross-flavor row).
NEO4J_DRIVER_FORCE_MRI: ${{ matrix.force_mri && '1' || '' }}
BUNDLE_JOBS: '1'
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false # no authenticated git ops here; don't leak the token to bundle
- uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
bundler-cache: true
- name: Wait for Memgraph (query-ready)
# A listening TCP port doesn't mean Memgraph accepts queries yet (and the
# startup user may not exist yet); retry a real authenticated query until
# it responds.
run: |
for i in $(seq 1 30); do
bundle exec ruby -e "require 'neo4j/driver'; Neo4j::Driver::GraphDatabase.driver('bolt://localhost:7687', Neo4j::Driver::AuthTokens.basic('neo4j', 'password')) { |d| d.session { |s| s.run('RETURN 1').consume } }" >/dev/null 2>&1 && exit 0
sleep 2
done
echo "::error::Memgraph never became query-ready"; exit 1
- name: Run RSpec (Memgraph)
run: bundle exec rspec --format progress
# Stable, version-free gate for branch protection. Requiring the matrix
# legs directly would embed the Ruby/Neo4j versions in the required-check
# names, so bumping the matrix would silently strip the requirement. This
# gate's name never changes; require `rspec-success` in the ruleset instead.
rspec-success:
name: rspec-success
needs: [rspec]
if: ${{ !cancelled() }}
runs-on: ubuntu-latest
steps:
- name: Require the rspec matrix to pass
if: ${{ needs.rspec.result != 'success' }}
run: |
echo "rspec matrix result: ${{ needs.rspec.result }}"
exit 1
# Version-free gate for the Memgraph leg. Not yet added to the branch-protection
# ruleset (Memgraph is community-verified, not gating) — promote by requiring
# `memgraph-success` once it has been stable for a while.
memgraph-success:
name: memgraph-success
needs: [rspec-memgraph]
if: ${{ !cancelled() }}
runs-on: ubuntu-latest
steps:
- name: Require the Memgraph suite to pass
if: ${{ needs.rspec-memgraph.result != 'success' }}
run: |
echo "rspec-memgraph result: ${{ needs.rspec-memgraph.result }}"
exit 1