Skip to content

feat(#4887): e2e-go Bolt conformance suite (neo4j-go-driver) - #4966

Merged
robfrank merged 13 commits into
mainfrom
feat/4887-e2e-go
Jul 4, 2026
Merged

feat(#4887): e2e-go Bolt conformance suite (neo4j-go-driver)#4966
robfrank merged 13 commits into
mainfrom
feat/4887-e2e-go

Conversation

@robfrank

@robfrank robfrank commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator

Part of epic #4882 (Bolt Driver Compatibility Certification), Group B. Closes #4887.

What

New standalone e2e-go/ module that certifies the official neo4j-go-driver/v5 against every scenario in the shared conformance spec bolt/conformance/spec.yaml (#4883), mirroring the merged e2e-python (#4885) and e2e-csharp (#4886) suites. Adds a PR-gating go-e2e-tests CI job.

Design / plan

  • Design: docs/superpowers/specs/2026-07-04-e2e-go-bolt-conformance-design.md
  • Plan: docs/superpowers/plans/2026-07-04-e2e-go-bolt-conformance.md

Coverage (all 39 scenarios)

One Test_<AREA>_<NNN>_<slug> function per spec.yaml scenario, so results are 1:1 comparable with the other language suites (traceability convention in bolt/conformance/README.md). Outcomes track each scenario's current_status:

  • 31 passing across connection, auth, transactions (incl. a two-writer transient-error race), causal-consistency (bookmarks), multi-database, result-handling, type round-trip, errors, protocol.
  • 6 documented gaps use a strict-xfail helper (assertStillFails, the Go port of C#'s KnownGapAssertions): RESULT-004 (empty summary counters), TYPE-003 (no native Path), TYPE-011 (Duration), TYPE-012 (Point), ERR-002 (semantic vs syntax error), PROTO-002 (no Bolt 5.x). Each passes while the gap reproduces and fails loudly (XPASS) if the gap is ever fixed, forcing a spec.yaml update in the same PR. All tracked by Bolt: tracking issue for protocol/type-fidelity gaps surfaced by certification #4890.
  • 2 skips: CONN-004 (needs a 3-node HA cluster) and ERR-003 (needs a raw-socket client - out of scope per the "official drivers only" principle).

Infrastructure

  • testcontainers-go, shared plain container via TestMain; data seeded over HTTP (never Bolt).
  • Lazy TLS-required/optional containers built from a derived image with a keytool-generated self-signed keystore/truststore baked in (CONN-002, CONN-005).
  • Honors ARCADEDB_DOCKER_IMAGE (defaults to arcadedata/arcadedb:latest).
  • New go-e2e-tests job in mvn-test.yml, modeled on csharp-e2e-tests, needs: build-and-package, PR-gating.

Verification

Local run (macOS) against the branch image: 36 PASS / 2 SKIP, all 6 gaps reproduced, zero XPASS. The single local CONN-003 failure is a Docker Desktop artifact - ArcadeDB's ROUTE response advertises the container's bridge IP, which is host-routable on the Linux CI runner (so CONN-003 passes in CI, exactly as in the Python/C# suites) but not on Docker Desktop. This is documented on the test and in e2e-go/README.md.

Scope

Multi-version driver-band matrix + nightly run (#4891), published compatibility matrix/badge (#4892), and the protocol/type-fidelity fixes themselves (#4890) are explicitly out of scope and deferred to their own issues.

robfrank added 6 commits July 4, 2026 15:32
Covers auth, transactions (incl. two-writer transient-error race),
causal-consistency, multi-database, result-handling, type round-trip,
errors, and protocol - plus the CONN-002/005 TLS tests. Six documented
gaps use the strict-xfail helper (RESULT-004, TYPE-003/011/012, ERR-002,
PROTO-002); CONN-004 and ERR-003 skip per the shared spec.
@mergify

mergify Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Tick the box to add this pull request to the merge queue (same as @mergifyio queue).

  • Queue this pull request

@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new standalone Go end-to-end test suite (e2e-go/) to certify the official neo4j-go-driver/v5 against the Bolt protocol conformance spec, complete with container fixtures, TLS testing capabilities, and a PR-gating CI job. The code review identified several critical improvements, including resolving potential container and temporary directory leaks on setup failures, optimizing HTTP payload reading by avoiding string allocations, and adding a timeout to the HTTP client to prevent indefinite hangs.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread e2e-go/fixtures_test.go
Comment on lines +122 to +133
return nil, err
}
bp, err := c.MappedPort(ctx, boltPort)
if err != nil {
return nil, err
}
return &arcadeContainer{container: c, host: host, httpPort: hp.Port(), boltPort: bp.Port()}, nil
}

func (c *arcadeContainer) httpBase() string {
return fmt.Sprintf("http://%s:%s", c.host, c.httpPort)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

If c.Host, c.MappedPort(ctx, httpPort), or c.MappedPort(ctx, boltPort) fails, the successfully started container c is leaked because it is never terminated. Ensure the container is terminated on any of these errors.

	host, err := c.Host(ctx)
	if err != nil {
		_ = c.Terminate(ctx)
		return nil, err
	}
	hp, err := c.MappedPort(ctx, httpPort)
	if err != nil {
		_ = c.Terminate(ctx)
		return nil, err
	}
	bp, err := c.MappedPort(ctx, boltPort)
	if err != nil {
		_ = c.Terminate(ctx)
		return nil, err
	}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed: startArcade now terminates the container on any Host()/MappedPort() failure after start.

Comment thread e2e-go/fixtures_test.go
Comment on lines +19 to +35
import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"strings"
"sync"
"testing"
"time"

"github.com/neo4j/neo4j-go-driver/v5/neo4j"
"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/wait"
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Add the "bytes" package to the import block to support using bytes.NewReader instead of strings.NewReader(string(body)) for better performance and fewer allocations.

Suggested change
import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"strings"
"sync"
"testing"
"time"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/wait"
)
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"strings"
"sync"
"testing"
"time"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/wait"
)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed: added the bytes import; httpCommand now uses bytes.NewReader(body).

Comment thread e2e-go/fixtures_test.go Outdated

// httpCommand POSTs a JSON body to an ArcadeDB HTTP endpoint with root auth.
func httpCommand(c *arcadeContainer, path string, body []byte) error {
req, err := http.NewRequest(http.MethodPost, c.httpBase()+path, strings.NewReader(string(body)))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Converting body from []byte to string and then wrapping it in strings.NewReader causes an unnecessary memory allocation. Use bytes.NewReader(body) directly instead.

Suggested change
req, err := http.NewRequest(http.MethodPost, c.httpBase()+path, strings.NewReader(string(body)))
req, err := http.NewRequest(http.MethodPost, c.httpBase()+path, bytes.NewReader(body))

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed: httpCommand uses bytes.NewReader(body), no []byte->string copy.

Comment thread e2e-go/fixtures_test.go Outdated
}
req.SetBasicAuth("root", rootPassword)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using http.DefaultClient without a configured timeout can cause the test suite to hang indefinitely if the server becomes unresponsive. It is highly recommended to use a custom http.Client with a reasonable timeout.

	client := &http.Client{Timeout: 30 * time.Second}
	resp, err := client.Do(req)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed: added a package-level http.Client with a 30s timeout (httpClient) and use it for seeding/create-db.

Comment thread e2e-go/tls_test.go
Comment on lines +55 to +59
dir, err := os.MkdirTemp("", "bolt-tls-certs")
if err != nil {
tlsImageErr = err
return
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If any subsequent step in buildTLSImage fails (such as runKeytool or docker build), the temporary directory dir is leaked because the package-level cleanup is only registered at the very end of the function. Use a deferred cleanup function that is disarmed only upon successful registration of the package-level cleanup.

		dir, err := os.MkdirTemp("", "bolt-tls-certs")
		if err != nil {
			tlsImageErr = err
			return
		}
		cleanupDir := true
		defer func() {
			if cleanupDir {
				_ = os.RemoveAll(dir)
			}
		}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed: buildTLSImage now registers a deferred os.RemoveAll(dir) guarded by cleanupDir, disarmed once the package-level cleanup takes ownership.

Comment thread e2e-go/tls_test.go
Comment on lines +96 to +100
tlsImageTag = tag
addTLSCleanup(func() {
_ = exec.Command("docker", "image", "rm", "-f", tag).Run()
_ = os.RemoveAll(dir)
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Disarm the local deferred cleanup once the package-level cleanup has been successfully registered.

		tlsImageTag = tag
		cleanupDir = false
		addTLSCleanup(func() {
			_ = exec.Command("docker", "image", "rm", "-f", tag).Run()
			_ = os.RemoveAll(dir)
		})

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed: cleanupDir is set false right before addTLSCleanup so ownership of dir passes to the package-level cleanup.

@github-advanced-security github-advanced-security AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Meterian found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.

@claude

claude Bot commented Jul 4, 2026

Copy link
Copy Markdown

PR Review: e2e-go Bolt conformance suite (#4887)

Reviewed the full Go module, the new go-e2e-tests CI job, and cross-checked every Test_<AREA>_<NNN> against bolt/conformance/spec.yaml and fixtures/type-matrix.cypher. Overall this is a clean, well-documented addition that faithfully mirrors the merged Python/C# suites. All 39 scenario ids line up 1:1 with spec.yaml, and the fixture properties referenced by the type tests (offsetDateTimeProp +02:00, nestedListProp, durationProp, pointProp, etc.) all exist. Nice work.

Strengths

  • Traceability: one function per scenario with the id embedded is exactly the convention in bolt/conformance/README.md; results stay comparable across languages.
  • assertStillFails xfail machinery (knowngap_test.go) is a faithful port of C#'s KnownGapAssertions, and the loud XPASS on fix is the right forcing function to keep spec.yaml honest.
  • Seeding over HTTP only (never Bolt) is correct - it keeps the serialization path under test out of the setup path.
  • Comments are genuinely useful (the CONN-003 bridge-IP explanation, the TLS build-context rationale, the Bolt-4.4-downgrade caveat at the package doc).
  • Dependencies are all test-only in an isolated module and license-compatible (neo4j-go-driver: Apache-2.0; testcontainers-go / testify: MIT); go.sum is committed for reproducibility.

Potential issues

  1. Flakiness risk in the two-writer race (TX-005 / ERR-004). raceTwoWriters asserts anyTransient(errs) is true. This is inherently timing-dependent: if ArcadeDB serializes the two commits cleanly (second blocks until first commits) instead of surfacing a Neo.TransientError.*, the test fails rather than the race not reproducing. The 500ms overlap makes a conflict likely and the comment acknowledges the sensitivity, but two scenarios depending on the same racy behavior is the most probable source of intermittent CI reds here. Worth watching; if it flakes, consider retrying the race a bounded number of times before failing.

  2. No -timeout on go test ./.... The default per-package test timeout is 10 minutes. This package starts the plain container (120s wait) plus lazily builds a derived image and boots two more TLS containers (150s wait each) on a shared CI runner. That can get close to 10m. Recommend an explicit -timeout 20m (or similar) on the CI command to avoid a spurious timeout kill.

  3. ARCADEDB_DOCKER_IMAGE: ${{ needs.build-and-package.outputs.image-tag }} references an output that doesn't exist. build-and-package declares no outputs:, so this expands to an empty string and imageName() falls back to arcadedata/arcadedb:latest (which the docker-load step populates), so it works. But it's misleading dead config - a pre-existing pattern copied from the java/python/csharp jobs. Either define the output once in build-and-package or drop the env from these jobs; not blocking, just flagging since the copy propagates it.

  4. Unbounded HTTP client in httpCommand. http.DefaultClient has no timeout, so a wedged server during createDatabase/seedTypeMatrix would hang TestMain until the CI job timeout rather than failing fast. A small http.Client{Timeout: ...} would be more robust for setup.

Minor / nits

  • Spec vs. test treatment for CONN-004. spec.yaml lists current_status: expected-fail, but the Go test is a t.Skip() (needs a 3-node cluster) rather than an assertStillFails xfail. Pragmatically correct - you can't exercise it single-node - but the skip's comment points at #4890 (the protocol/type-gap tracker) whereas this is really a cluster/infra limitation. Consider aligning the referenced issue and/or the spec current_status so the skip vs. xfail rationale is unambiguous.
  • repoRoot() panics if bolt/conformance isn't found. Since it's reached via TestMain, a log.Fatalf / returning an error would give a cleaner failure than a panic (the deferred Terminate still runs on unwind, so this is cosmetic).
  • Shared-container write accumulation: tests mutate the beer DB (e.g. TX-004, RESULT-004 create nodes). This is safe because scenarios use unique markers and Go runs these sequentially (no t.Parallel), but it's worth a one-line note that the isolation guarantee rests on the no-parallel assumption.

None of these are blockers. Items 1 and 2 are the ones most likely to bite in CI; the rest are polish.

🤖 Generated with Claude Code

robfrank added 2 commits July 4, 2026 15:57
…bumps

- startArcade: terminate the container if Host/MappedPort fails after start
- buildTLSImage: deferred temp-dir cleanup, disarmed once package-level
  cleanup takes ownership
- httpCommand: shared http.Client with a 30s timeout; bytes.NewReader(body)
  instead of strings.NewReader(string(body))
- bump vulnerable transitive deps flagged by the dependency scan
  (testcontainers-go 0.38.0, golang.org/x/net 0.56, x/crypto 0.53,
  grpc 1.82, protobuf 1.36.11)
- raceUntilConflict: retry the race (distinct marker per attempt) only when
  no conflict reproduced, up to 5 tries; a surfaced non-transient error still
  fails the assertion rather than being retried away (TX-005, ERR-004)
- go-e2e-tests: run go test with -timeout 20m (plain + 2 TLS containers plus
  a derived-image build run sequentially, near the 10m default)
- document the shared-container isolation assumption (unique markers + no
  t.Parallel)
@claude

claude Bot commented Jul 4, 2026

Copy link
Copy Markdown

Review: e2e-go Bolt conformance suite

Nicely structured suite. The 1:1 Test_ naming, the strict-xfail assertStillFails helper, and the detailed doc comments explaining the CONN-003/PROTO-002 gaps make this very readable and maintainable, and it mirrors the Python/C# suites well. A few things worth addressing.

Go version mismatch (likely to affect CI reproducibility)
e2e-go/go.mod declares go 1.25.0, but the new go-e2e-tests job pins go-version: "1.23" (.github/workflows/mvn-test.yml line 513). With the default GOTOOLCHAIN=auto, running go test under a 1.23 toolchain against a module requiring 1.25.0 silently auto-downloads and switches to the go1.25.0 toolchain. That works only when the download succeeds, makes the setup-go version and its module cache misleading, and breaks outright under GOTOOLCHAIN=local. Two clean fixes:

  • Preferred: drop the hardcoded version and use go-version-file: e2e-go/go.mod so CI always tracks the module declaration, or
  • Bump go-version to "1.25" (and consider whether the go directive needs the .0 patch suffix; go 1.25 is more conventional).

Race-based tests may be flaky in CI (TX-005 / ERR-004)
raceTwoWriters depends on two writers actually colliding (a time.Sleep(500ms) inside an open tx), and both Test_TX_005 and Test_ERR_004 hard-assert require.True(anyTransient(errs)). If the two transactions ever serialize cleanly (no transient conflict surfaced), these fail rather than skip. The comment acknowledges the timing sensitivity, but a hard assertion on a race is a classic CI flake source. Consider bounding the risk (a bounded retry of the race, or downgrading a no-conflict-observed outcome to t.Skip/log rather than a failure) so an unlucky scheduling window does not red the whole PR gate. Worth watching the first few CI runs regardless.

Minor

  • Shared-DB mutation without cleanup: several tests write into the seeded beer DB with fixed markers/names (TxCommitProbe, Beer{name:TX-004-Beer}, RESULT-004-Beer, CausalProbe, RaceProbe). Assertions like Test_TX_004 count==1 are only correct because the container is ephemeral per suite run; a second run against the same container would double-count. Fine given TestMain fresh container, but a one-line note (or unique markers) would make the invariant explicit.
  • Job timeout: the go-e2e-tests job has no timeout-minutes. With container startup (120s) plus a lazily-built TLS image, a hung pull/build would run to the default 6h GH limit. A modest timeout-minutes guards the runner.
  • Dependencies/licensing: neo4j-go-driver (Apache-2.0), testify (MIT), testcontainers-go (MIT) are all allowed per CLAUDE.md. These are test-only Go modules not shipped in the Java distribution, so ATTRIBUTIONS.md likely does not need updating; just confirm that matches how e2e-python/e2e-csharp were handled.

Good practices worth calling out

  • require/error-collection is correctly kept out of the racing goroutines (only the main goroutine touches t), avoiding the common t.Fatal-from-goroutine pitfall.
  • run(m)/TestMain split so deferred container teardown runs before os.Exit, and lazy TLS containers with LIFO cleanup - clean resource handling.
  • assertStillFails failing loudly on XPASS (forcing a spec.yaml update in the same PR) is a great regression guard.

Overall this is solid; the Go version alignment is the one item I would fix before merge, and I would keep an eye on the two race tests.

@robfrank

robfrank commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed in 48bf6d3 (and 46a057d for #4, which landed before this review ran):

  1. Race flakiness (TX-005/ERR-004) - added raceUntilConflict: retries the race with a distinct marker each attempt (up to 5) but only when no error was surfaced (clean serialization). As soon as any error appears it returns, so a genuine non-transient code still fails the assertion rather than being retried away. Both scenarios now use it.
  2. go test -timeout - the go-e2e-tests job now runs go test -v -timeout 20m ./..., with a comment explaining the sequential container startups.
  3. ARCADEDB_DOCKER_IMAGE -> non-existent output - correct that it resolves to empty and falls back to arcadedata/arcadedb:latest (populated by docker load), so it works. Keeping it as-is for consistency: the java/python/csharp e2e jobs all use the identical expression, and defining the output on build-and-package is a shared-CI change beyond this issue's scope. The intentional fallback is documented on imageName().
  4. Unbounded HTTP client - already fixed in 46a057d: a package-level http.Client{Timeout: 30s} is used for seeding/create-db.

Nits:

  • CONN-004 skip vs xfail / Bolt: tracking issue for protocol/type-fidelity gaps surfaced by certification #4890 - the #4890 reference matches spec.yaml's own tracking_issue for CONN-004 (the HA-aware ROUTE gap is tracked there), and the skip mirrors the Python/C# suites, which also can't exercise a 3-node cluster single-node. Left aligned with the spec + siblings.
  • repoRoot() panic - kept; it only runs in TestMain setup and the message is explicit, and (as noted) the deferred Terminate still runs on unwind.
  • Shared-container write accumulation - added a comment on plainContainer documenting that isolation rests on unique markers + sequential (no t.Parallel) execution.

Full suite still green locally (36 pass / 2 skip, 6 gaps reproduce, 0 XPASS); CONN-003 is the CI-only/Linux scenario per the note on the test.

- use go-version-file: e2e-go/go.mod so setup-go tracks the module's go
  directive (bumped to 1.25 by the dependency updates) instead of a
  hardcoded version that drifts and forces a GOTOOLCHAIN auto-download
- add timeout-minutes: 30 to guard against a hung image pull/build
@robfrank

robfrank commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator Author

Addressing this review (of 46a057d):

Go version mismatch - real issue, caused by the dependency bumps in 46a057d pushing the go.mod directive to go 1.25.0. Fixed in 6589c74 with your preferred option: the go-e2e-tests job now uses go-version-file: e2e-go/go.mod, so setup-go always tracks the module's declaration and can't drift again.

Race flakiness (TX-005 / ERR-004) - addressed in 48bf6d3 (just before this review): raceUntilConflict retries the race up to 5 times but only when no error surfaced (clean serialization). A surfaced non-transient code still fails the assertion rather than being retried away, so it doesn't mask a real regression.

Job timeout - added timeout-minutes: 30 to the job in 6589c74 (distinct from the -timeout 20m on go test, which is the per-package deadline).

Shared-DB mutation - added a comment on plainContainer (48bf6d3) documenting that the invariant rests on unique markers + sequential (no t.Parallel) execution against the ephemeral per-suite container.

Dependencies/licensing - confirmed: all three are test-only Go modules (Apache-2.0 / MIT), the module is outside the Maven reactor and not shipped in the distribution, so ATTRIBUTIONS.md is untouched - matching how e2e-python and e2e-csharp were handled (neither updated it).

@claude

claude Bot commented Jul 4, 2026

Copy link
Copy Markdown

PR Review: e2e-go Bolt conformance suite

Reviewed the full diff. This is a high-quality, well-documented test-only addition that mirrors the existing e2e-python/e2e-csharp suites closely. The Test_ traceability convention, the strict-xfail (assertStillFails) helper for documented gaps, and the sequential shared-container design are all clean and consistent with the sibling suites. No production Java code is touched, so blast radius is limited to CI. Feedback below, ordered by importance.

[Should fix] go.mod go directive (1.25.0) does not match the CI toolchain (1.23)

e2e-go/go.mod declares go 1.25.0, but .github/workflows/mvn-test.yml pins setup-go to go-version: "1.23" (the design plan and README both describe the intent as go 1.23). With the default GOTOOLCHAIN=auto, Go 1.23 will silently download the 1.25 toolchain at test time to satisfy the directive - an unintended network dependency the setup-go cache will not cover. In any environment with GOTOOLCHAIN=local (or an air-gapped runner), go test fails hard with "go.mod requires go >= 1.25.0". Please align: either bump go-version to "1.25" (keeping the cache) or lower the go.mod directive to 1.23. The workflow comment ("Track the module go directive so the toolchain always matches") shows they were meant to stay in lockstep, so they have drifted.

[Reliability] two-writer race can still be flaky (TX-005, ERR-004)

raceUntilConflict retries up to 5 times only when no error surfaces, a nice hardening over a single attempt. But the test still hard-fails (require.NotEmpty) if the engine serializes both writers cleanly on all 5 attempts. Since the scenario hinges on a 500ms-sleep timing window, there is a residual flake risk on a loaded CI runner. Given the suite is PR-gating, consider whether a clean-serialization outcome should be a t.Skip/soft-pass rather than a failure - "no conflict reproduced" is not itself a conformance violation. At minimum it is worth documenting as a known flake source.

[Reliability] external-network dependency at container startup

The beer database is imported from a GitHub raw URL (OpenBeer.gz) via defaultDatabases, and readiness is gated only on /api/v1/ready returning 204. Every scenario depends on that fetch succeeding and on the import finishing before the readiness probe passes. This matches the Python/C# suites so it is not a regression, but a GitHub outage turns the whole PR-gating job red. Flagging the coupling, not blocking.

Minor notes

  • Test_TYPE_010 hardcodes the +02:00 offset (26060); I confirmed bolt/conformance/fixtures/type-matrix.cypher uses +02:00, so it is correct but implicitly coupled to that fixture value.
  • The derived TLS image uses a fixed tag arcadedb-bolt-tls-go:latest. Fine for isolated CI runners; could collide if two runs ever shared a Docker daemon. Cleanup via docker image rm -f is good.
  • Nice touches: startArcade terminates the container on Host/MappedPort failure (no leak), httpClient has a 30s timeout, run(m) wraps TestMain so deferred teardown runs before os.Exit, and the TLS temp-dir ownership transfer (cleanupDir = false) is handled correctly.

Test coverage / conventions

All 39 spec.yaml scenarios are represented 1:1, outcomes track current_status, and the 6 documented gaps use the strict-xfail helper that fails loudly on XPASS - correctly forcing a spec.yaml update whenever a gap gets fixed. Apache headers are present on every .go file with no debug/attribution leftover, per the repo conventions.

Overall: solid work. The go.mod/CI version mismatch is the one item I would want resolved before merge; the rest are judgment calls.


Automated review by Claude (Opus 4.8). Focused on the diff; could not execute the Go suite in this environment.

assertConflictTransient skips TX-005/ERR-004 when the bounded retries never
surface a conflict (a scheduling artifact on a loaded runner, not a
conformance violation) instead of hard-failing the PR gate. When a conflict
does surface it must still be a retryable Neo.TransientError.* and never a
non-retryable ClientError/DatabaseError.
@robfrank

robfrank commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks for the thorough pass. This review is of 48bf6d3; addressing it:

go.mod (1.25) vs CI (1.23) - already resolved in 6589c74 (pushed just after this review ran): the go-e2e-tests job now uses go-version-file: e2e-go/go.mod, so the toolchain always tracks the module directive. The 1.25 bump was a consequence of the security-driven dependency updates; keeping the patched deps and letting CI follow go.mod is the intended lockstep. (README is version-agnostic; no 1.23 hardcoded there.)

Two-writer race soft-pass - good call for a PR gate. bb888fd: assertConflictTransient now t.Skips TX-005/ERR-004 when the bounded retries never surface a conflict ("no conflict reproduced" is not a conformance violation), while still asserting that any conflict that does surface is a retryable Neo.TransientError.* and never a non-retryable client/database error.

External-network dependency (OpenBeer.gz import) - agreed it's a coupling; left as-is since it's identical to the Python/C# fixtures (not a regression), but flagging it forward.

Minor notes - TYPE-010's +02:00 is intentionally coupled to type-matrix.cypher's offsetDateTimeProp (the shared fixture); the TLS image tag collision risk only matters on a shared Docker daemon, which the isolated CI runners aren't. Left both as-is.

Full suite still green locally (36 pass / 2 skip / 6 gaps reproduce / 0 XPASS).

@claude

claude Bot commented Jul 4, 2026

Copy link
Copy Markdown

Review: e2e-go Bolt conformance suite

Reviewed the full diff. This is a clean, test-only addition (no production Java touched), well-documented, and it faithfully mirrors the merged e2e-python/e2e-csharp suites. The 1:1 Test_ traceability, the strict-xfail helper, and HTTP-only seeding (never over Bolt) are all the right calls. Nice work. Observations below are mostly minor/nits, none blocking.

Correctness / potential bugs

  • assertStillFails is lenient by design (worth a caveat). The helper treats any non-nil error from body as "gap still reproduces." For the query-based gap tests (TYPE-003/011/012, RESULT-004, ERR-002, PROTO-002) a transient infra hiccup (connection blip, container pressure) is indistinguishable from the real gap and keeps the test green. Inherent to the ported pattern and matches C#/Python, so acceptable, but these six tests cannot detect a regression that changes the failure mode. ERR-002 is the sharpest case: if the query ever returned no error at all, the body returns "expected an error, got none", which still counts as "gap reproduces" even though the documented cause (mapped as SyntaxError) no longer holds. Consider asserting the specific wrong code/type where feasible, so only the intended gap keeps them green.

  • TYPE-010 unchecked type assertion. require.WithinDuration(t, tm, e.(time.Time), 0) panics (not fails cleanly) if the echoed value is not a time.Time, and tolerance 0 requires exact-instant equality on the round trip. If ArcadeDB ever truncates sub-second precision on the echo path this becomes brittle. A guarded e2, ok := e.(time.Time); require.True(t, ok, ...) plus a small tolerance would be more robust.

  • CONN-004 skip message points at 4890. The skip text says "see Bolt: tracking issue for protocol/type-fidelity gaps surfaced by certification #4890", but 4890 is the protocol/type-fidelity fixes issue; CONN-004 is skipped because it needs 3-node HA orchestration. Worth pointing at the HA/cluster issue to avoid confusion.

Flakiness (CI)

  • Two-writer race (TX-005 / ERR-004). The bounded raceUntilConflict (5 attempts, distinct marker each) is good hardening, and returning as soon as any error surfaces (so a non-transient code still fails the assertion) is exactly right. Residual risk: if the engine serializes both writers cleanly on all 5 attempts, require.NotEmpty(errs) fails the run. On a loaded shared runner that is plausible. Both tests exercise the same mechanism back-to-back with 500ms held transactions - keep an eye on CI stability; if it flakes, raising the attempt ceiling or shrinking the hold window helps more than the current sleep.

  • Shared-container write accumulation. RESULT-004 (CREATE, not MERGE), the race probes, and TX-004 all write into the shared beer DB, while type tests do MATCH (b:Beer) ... LIMIT 1. Works today via source-order execution + unique markers, and the fixtures comment documents this. Flagging that any future count-based assertion on Beer/RaceProbe would be fragile against these residues; the fresh-DB guidance in the fixtures comment is the right mitigation.

Minor / nits

  • docker build / docker image rm use a static tag arcadedb-bolt-tls-go:latest. Fine on ephemeral GitHub runners; would collide if two suite runs shared a Docker daemon. Noting only.
  • go 1.25.0 directive is bleeding-edge; using go-version-file for setup-go is the correct way to keep the toolchain aligned - just be aware the auto-download is now pinned to whatever the dep bumps push go.mod to.
  • Resource-leak guards in startArcade (terminate on Host/MappedPort failure) and the cleanupDir ownership handoff in buildTLSImage are handled correctly - good attention to detail.

Test coverage

Complete against the 39-scenario spec: 31 passing, 6 documented gaps via strict-xfail, 2 justified skips, all tied to spec.yaml and 4890. The XPASS guard forcing a spec.yaml update when a gap is fixed is a nice forcing function.

Overall: LGTM. The items above are refinements, not blockers. Main thing to watch post-merge is CI stability of the two race-based tests.

Pulls github.com/docker/docker v28.2.2 -> v28.5.2, clearing the two high
severity transitive advisories (CVE-2026-34040, CVE-2026-33997) flagged by
the dependency scanner. Test-only module, not shipped in the distribution.
Full suite re-verified green.
@robfrank

robfrank commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator Author

On the Meterian scan: it flagged transitive dependencies of testcontainers-go (all test-only, in an isolated module not shipped in the distribution). Addressed the two high findings in 7920484 by bumping testcontainers-go v0.38.0 -> v0.43.0, which pulls docker/docker v28.2.2 -> v28.5.2 and clears CVE-2026-34040 / CVE-2026-33997. Full suite re-verified green after the bump (TLS scenarios included).

The remaining medium/low advisories are inherent to the Docker/OpenTelemetry transitive tree that any testcontainers-based suite pulls in - the same class the merged e2e-python/e2e-csharp testcontainers suites carry - and the scan is advisory (main is not a protected branch). Not chasing those on a test-only module.

@claude

claude Bot commented Jul 4, 2026

Copy link
Copy Markdown

Review: e2e-go Bolt conformance suite (#4966)

Overall this is a high-quality, well-scoped PR. It faithfully mirrors the merged e2e-python/e2e-csharp suites, keeps a clean 1:1 Test_<AREA>_<NNN>_<slug> mapping to spec.yaml, and the strict-xfail (assertStillFails) guard for documented gaps is a genuinely good pattern: it turns a "known limitation" into a tripwire that fails loudly the moment the gap is fixed, forcing the spec.yaml update in the same PR. The commit history also shows the review-hardening work (resource-leak guards, HTTP client timeout, dependency vuln bumps, CI toolchain alignment, race-retry) already landed. Since this is a test-only, out-of-reactor module, there is no Java compilation impact.

A few observations, all minor:

1. Two-writer race can now silently SKIP a regression (TX-005 / ERR-004)

assertConflictTransient (patch 10) t.Skips when no conflict reproduces after the 5 bounded retries. That is the right call to avoid flaky PR gating, but note the blind spot: if ArcadeDB ever regressed to no longer surfacing write conflicts at all (lost isolation), these two scenarios would skip forever rather than fail, and nobody would notice the conformance loss. Consider either (a) a loud t.Logf that distinguishes "runner too fast/loaded" from a suspicious persistent no-conflict, or (b) a lightweight post-race sanity assertion that the writes were at least serialized. Not a blocker.

2. go 1.25.0 directive in go.mod

The dependency bumps pushed the module go directive to 1.25.0 (a very fresh toolchain). CI now correctly tracks it via go-version-file: e2e-go/go.mod, so this is consistent - good fix. Just confirm 1.25.0 was intentional and not an artifact of go mod tidy running on a 1.25 machine; if the code only needs 1.23 semantics, pinning lower reduces the toolchain-download surface. Non-blocking.

3. CONN-004 skip message references #4890

The skip text says "see #4890", but #4890 is the server-side protocol/type-fidelity issue, whereas CONN-004 is a 3-node HA-cluster gap (the design doc frames HA orchestration separately). Worth pointing this skip at the correct HA tracking issue so traceability stays accurate.

4. GITHUB_TOKEN in the go-e2e-tests job env appears unused

The dataset (OpenBeer.gz) is fetched server-side inside the container from github raw, and the token is not propagated into the container; the Go suite itself makes no authenticated GitHub calls. It looks carried over from the C# job template. Harmless, but could be dropped to avoid implying a dependency that isn't there.

5. CI runs go test only - consider adding go vet

The implementation plan called for gofmt -l / go vet ./..., but the CI job only runs go test -v ./.... A go vet ./... step is cheap and would catch mistakes (e.g. bad Printf verbs, a dropped Cleanup) that go test won't. Optional.

Things done well

  • Seeding strictly over HTTP (never Bolt) so the serialization path under test is never used to set up fixtures.
  • Lazy TLS containers behind sync.Once with LIFO cleanup, keytool-generated throwaway certs baked into a derived image, and graceful t.Skip when keytool/docker are absent locally.
  • Container start failures terminate the container on the Host/MappedPort error paths (no leaked containers).
  • assertStillFails bodies correctly use the error-returning runSingleErr variant rather than t.Fatal, so the XPASS guard actually works.
  • Documented, honest note about CONN-003 neo4j:// routing only being fully verifiable in CI (Docker bridge IP routability).

Nice work - the module is comprehensive and the gap-handling discipline is exactly what a conformance suite needs.

@claude

claude Bot commented Jul 4, 2026

Copy link
Copy Markdown

PR Review: e2e-go Bolt conformance suite

Reviewed the full diff. This is a clean, well-documented, test-only module that faithfully mirrors the existing e2e-python/e2e-csharp suites. The traceability naming (Test_), the strict-xfail helper, the lazy TLS fixtures, and the CI wiring are all consistent with the sibling suites. No Java/engine code is touched, so there is no compile or runtime risk to the database itself. Nice work overall - the inline comments explaining why (CONN-003 routing, shared-container isolation, downgrade dependency) are genuinely helpful.

A few observations, roughly in priority order.

1. TX-005 / ERR-004 can silently certify nothing (main concern). assertConflictTransient calls t.Skip when the two-writer race never surfaces a conflict after the 5 bounded retries, and raceUntilConflict returns early only on an error. On a runner where ArcadeDB serializes the two writers cleanly (no abort), BOTH transient-error scenarios skip and the transient-error contract - the whole point of these two spec scenarios - is never actually exercised, while the PR gate stays green. The soft-skip is a reasonable anti-flake measure, but combined with the fixed 500ms hold it risks these becoming permanent no-ops without anyone noticing. Worth considering: (a) emit a t.Logf (visible even on skip with -v) making it explicit that the conformance assertion was NOT verified this run, so a green run is not mistaken for certified; (b) if the conflict is not reliably reproducible against ArcadeDB locking, a more deterministic construction (hold writer A tx open, then have writer B attempt and assert its error synchronously) may certify the behavior without depending on scheduler timing.

2. Shared beer DB accumulates write side-effects. TX-004 (TX-004-Beer), RESULT-004 (RESULT-004-Beer/Brewery), and each raceUntilConflict attempt (RaceProbe per distinct marker) permanently mutate the shared beer database. This is documented (the fixtures comment says count-based assertions must use a fresh DB) and safe because the container is ephemeral per run. Just flagging that it makes the suite order-and-rerun sensitive against any non-ephemeral target; the current LIMIT-based reads avoid trouble, but future scenarios should keep using boltscratch / a fresh DB rather than beer for anything count-sensitive.

3. MDB-002 leaks the open transaction on a mid-test assertion failure. In Test_MDB_002 the require.Equal(t, int64(0), c) isolation check runs between BeginTransaction and Commit, with no t.Cleanup/defer for tx or the scratch session. If that assertion fails, require calls runtime.Goexit and the open transaction is never committed/rolled back and the session is not closed (the driver Close cleanup eventually reclaims it, so low severity). A defer sess.Close(ctx) / t.Cleanup on the scratch session would make failure teardown clean and match the pattern used elsewhere in the file.

4. TLS scenarios depend on the runner preinstalled keytool. The go-e2e-tests job sets up only Go, not a JDK, so CONN-002/CONN-005 rely on ubuntu-latest shipping a keytool on PATH (README claims they always run in CI). This matches the python/csharp jobs (they do not set up Java either), so it is consistent - but it is a silent-skip failure mode: if the runner image ever drops the default JDK from PATH, these two silently skip instead of failing. An explicit actions/setup-java step (or a CI assertion that keytool exists) would harden the always-run-in-CI guarantee. Minor / optional.

Minor notes. repoRoot() panics on failure and is reached from TestMain via seedTypeMatrix; the defer c.container.Terminate in run() still executes during unwind, so no container leak - just noting the asymmetry with the error-returning style of the surrounding helpers. go.mod pins go 1.25.0 (patch-level directive); using go-version-file in CI is the right call to avoid a GOTOOLCHAIN auto-download surprise. Dependency hygiene is good: pinned SHAs for actions, testcontainers-go bumped to clear the docker/docker CVEs, and transitive x/net / x/crypto / grpc bumps. All Apache-2.0/BSD/MIT compatible - no license concern for a test-only module.

Verdict. Solid, low-risk addition that extends conformance coverage to the Go driver with strong cross-language traceability. My only substantive ask is item 1 (make the transient-error scenarios visibly signal when they did not actually assert anything); the rest are polish. LGTM once item 1 is at least acknowledged.

@codacy-production

codacy-production Bot commented Jul 4, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Coverage ∅ diff coverage · -6.53% coverage variation

Metric Results
Coverage variation -6.53% coverage variation
Diff coverage diff coverage

View coverage diff in Codacy

Coverage variation details
Coverable lines Covered lines Coverage
Common ancestor commit (c0dbdc8) 134228 99739 74.31%
Head commit (b22197d) 166031 (+31803) 112525 (+12786) 67.77% (-6.53%)

Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch: <coverage of head commit> - <coverage of common ancestor commit>

Diff coverage details
Coverable lines Covered lines Diff coverage
Pull request (#4966) 0 0 ∅ (not applicable)

Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified: <covered lines added or modified>/<coverable lines added or modified> * 100%

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@codecov

codecov Bot commented Jul 4, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 66.06%. Comparing base (c0dbdc8) to head (b22197d).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@             Coverage Diff              @@
##               main    #4966      +/-   ##
============================================
+ Coverage     65.32%   66.06%   +0.74%     
============================================
  Files          1682     1682              
  Lines        134228   134228              
  Branches      28712    28712              
============================================
+ Hits          87685    88679     +994     
+ Misses        34526    33379    -1147     
- Partials      12017    12170     +153     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

… keytool guard

- assertConflictTransient logs a loud 'NOT CERTIFIED THIS RUN' line before
  skipping, so a clean-serialization skip can't be mistaken for a certified
  green (TX-005/ERR-004)
- Test_MDB_002: t.Cleanup on the scratch session so a mid-test isolation
  assertion failure can't leak the open transaction
- go-e2e-tests: assert keytool is on PATH before running, so the TLS
  scenarios fail loudly instead of silently skipping if the runner ever drops
  the JDK
@robfrank

robfrank commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed in 0ed2544:

1. Transient-error scenarios could silently certify nothing - the real risk introduced by the soft-skip. assertConflictTransient now emits a loud t.Logf("NOT CERTIFIED THIS RUN: ...") before skipping, so a clean-serialization skip is visible under -v and a green run isn't mistaken for a certified one. On (b), the deterministic hold-A/attempt-B construction: under ArcadeDB's locking, B would likely block until A commits and then succeed, rather than aborting with a transient error - so it may not certify the same Neo.TransientError.* contract the timing race does (DeadlockDetected reproduces on attempt 1 every local run). Kept the race + loud-skip rather than change what's certified.

3. MDB-002 open-tx leak on mid-test failure - added t.Cleanup(func(){ _ = scratch.Close(ctx) }) right after creating the scratch session, so a failure of the isolation require between begin and commit releases the session/tx cleanly (driver Close is idempotent, so it composes with the explicit close later).

4. TLS keytool dependency - added a command -v keytool guard step to the go-e2e-tests job before the test run, so CONN-002/005 fail loudly instead of silently skipping if the runner image ever drops the JDK from PATH. Kept it as a guard rather than a full setup-java to avoid an unnecessary JDK download.

2. Shared beer DB accumulation - already documented on plainContainer (count-sensitive scenarios must use boltscratch/a fresh DB); no code change.

Minor notes (repoRoot panic, go 1.25 directive) left as-is - the panic only fires in TestMain setup with the deferred container teardown still running, and go-version-file already handles the toolchain. Touched tests re-run green.

@claude

claude Bot commented Jul 4, 2026

Copy link
Copy Markdown

PR Review: e2e-go Bolt conformance suite (#4966)

Reviewed the full diff. This is a clean, unusually well-documented test-only module that mirrors the existing e2e-python/e2e-csharp suites. Scenario coverage checks out (39 functions: CONN×5, AUTH×3, TX×5, CAUSAL×1, MDB×2, RESULT×4, TYPE×12, ERR×4, PROTO×3), the container/cleanup lifecycle is correct (run(m) wrapper so deferreds fire before os.Exit, startArcade terminates the container on partial init failure, HTTP client timeout, seeding over HTTP rather than the Bolt path under test). Nice work. A few points, mostly low severity.

Correctness / robustness

  1. assertStillFails treats any error as "gap reproduced" (medium). In knowngap_test.go, body() returning a non-nil error is interpreted as the known gap still reproducing. But the bodies for TYPE-003/011/012, ERR-002, RESULT-004, and PROTO-002 run real queries via runSingleErr/Consume, so an infrastructure failure (server hiccup, dropped connection, auth blip) also returns an error and is silently logged as known gap still reproduces (expected) -> green. That means if the server were partially broken, these 6 tests would falsely certify while the rest go red. The C# KnownGapAssertions port likely shares this, but consider distinguishing "the query ran and produced the wrong-but-expected shape" from "the call failed for unrelated reasons" - e.g. require the body to reach the type-assertion/value-mismatch path rather than accepting a bare driver/transport error. At minimum worth a comment acknowledging the limitation.

  2. Test_TX_004 is not re-run safe (low-medium). It creates (:Beer {name:'TX-004-Beer'}) and asserts count == 1. fixtures_test.go already documents that isolation rests on unique per-test markers + sequential execution, but TX-004 is exactly the "count-based assertion" that comment warns will break - go test -count=2 or any container reuse fails it. A per-run-unique marker (or the fresh-db approach the comment recommends) would make it robust. RESULT-004's xfail body also creates 2 nodes per run, though it doesn't assert a DB-wide count so it's harmless today.

CI / tooling

  1. Consider -race in the CI job (low). raceTwoWriters spawns genuine goroutines with shared errs/mu. The harness synchronization looks correct, but running go test -race would both strengthen the concurrency scenarios and guard the harness itself against future data-race regressions. Cheap insurance for a suite whose whole point is concurrency conformance.

  2. go.mod declares go 1.25.0 while the plan doc says go 1.23. Doc-only drift (the CI setup-go uses go-version-file, so it's consistent in practice), but worth reconciling the plan text.

  3. Dependency attributions (low). CLAUDE.md asks to update ATTRIBUTIONS.md when adding dependencies. The new deps (neo4j-go-driver Apache-2.0, testcontainers-go MIT, testify MIT, transitive BSD) are all license-compatible, and this is a standalone test module outside the Maven reactor / shipped distribution, so it is arguably N/A - just confirm that is the intended call.

Nits

  1. TX-005/ERR-004 skip (loudly, with a NOT CERTIFIED THIS RUN log) when the write conflict doesn't reproduce within the bounded retries. Reasonable trade-off against flakiness and clearly surfaced - no change needed, just flagging that a persistent clean-serialization regression could hide behind an all-green gate.
  2. Test_CONN_003/004 are defined before Test_CONN_002 - cosmetic ordering only.
  3. Test_MDB_002 closes scratch explicitly and via t.Cleanup; the double close is harmless (driver Close is idempotent) and intentional for the failure path - fine.

Overall: solid, ready to merge after considering #1 and #2. The rest are optional hardening.

Weekly gomod ecosystem entry for /e2e-go so the new Go test module's
dependencies (notably the testcontainers-go transitive tree that carried the
docker/docker CVEs) get automated update PRs. Minor/patch grouped into one PR
to limit noise; majors open individually.
@claude

claude Bot commented Jul 4, 2026

Copy link
Copy Markdown

Review: e2e-go Bolt conformance suite (PR #4966)

Reviewed the new e2e-go/ module, the go-e2e-tests CI job, and the dependabot entry against bolt/conformance/spec.yaml and the sibling e2e-python/e2e-csharp suites. This is a high-quality, well-documented addition. Scenario coverage is genuinely 1:1 with the spec, the strict-xfail mechanism is a faithful port of C-sharp KnownGapAssertions, and comments consistently explain why rather than what. Findings below, mostly minor.

Strengths

  • Traceability is exact. All 39 Test_<AREA>_<NNN>_<slug> functions map cleanly to spec.yaml, and I verified current_status handling matches (passing/unverified -> green assert, expected-fail -> assertStillFails, HA/raw-socket -> t.Skip). The assertStillFails XPASS guard forcing a spec.yaml update in the same PR is a nice anti-rot mechanism.
  • Correct Go test concurrency. In raceTwoWriters, the spawned goroutines collect errors under a mutex instead of calling require.* (which calls runtime.Goexit and is only safe on the test goroutine). A common trap, avoided correctly.
  • Clean container lifecycle: run() isolates setup/teardown so defers fire before os.Exit, teardown order is LIFO-correct (TLS containers before the plain one), and the temp cert-dir ownership handoff in buildTLSImage (cleanupDir flag) is careful.
  • Seeding over HTTP, never Bolt correctly keeps the serialization path under test out of fixture setup.
  • Licensing is fine: neo4j-go-driver (Apache-2.0), testcontainers-go (MIT), testify (MIT) all satisfy CLAUDE.md. Not adding these to ATTRIBUTIONS.md is consistent with the non-shipped, out-of-reactor e2e-python/e2e-csharp siblings.

Things worth addressing

  1. Test_TYPE_010 can panic instead of failing cleanly. require.WithinDuration(t, tm, e.(time.Time), 0) does an unchecked type assertion on the echoed value. If the driver returns anything but time.Time, this panics rather than producing the readable "expected time.Time, got %T" message the other TYPE tests use. Prefer the comma-ok form + require.True(t, ok, ...) for symmetry with Test_TYPE_007/008/009.

  2. TX-005 / ERR-004 can silently self-skip on CI. assertConflictTransient skips when the write conflict never reproduces across the 5 bounded retries. The loud t.Logf("NOT CERTIFIED THIS RUN...") is good defensive design, but it means these two scenarios can go green-without-actually-asserting on a loaded runner, and a permanent regression to clean serialization would show only as a SKIP + log line, not a failure. Acceptable for a timing race, but consider whether 5 retries is enough on a busy runner, and note the risk is invisible unless someone reads -v output. (Not a blocker; the siblings make the same tradeoff.)

  3. Test_MDB_002 isolation is really cross-database separation. The mid-transaction require.Equal(int64(0)) on beer is trivially true because beer and boltscratch are distinct databases; beer would never see a boltscratch write regardless of commit state. This matches the spec MDB-002 intent (cross-db isolation), so it is fine, but the inline comment about beer not seeing the uncommitted boltscratch write slightly oversells what is proven (it would not see a committed one either). Minor wording.

  4. CONN-003 fails locally on Docker Desktop by design. Documented thoroughly in the test and README, mirroring Python/C-sharp - but it does mean go test ./... is not cleanly green for a Mac/Windows dev running the full suite locally. Worth keeping in mind; no change requested.

Minor / nits

  • go.mod pins go 1.25.0 while the plan/design docs reference Go 1.23. Not a problem (CI uses go-version-file), just drift between the committed plan and the final module.
  • Shared-beer writes (TX-002/004, RESULT-004, CAUSAL-001, race probes) accumulate marker nodes across the run. All reads are marker- or LIMIT-scoped so it is safe, and the fixture comment already warns that any future count-based assertion on beer must move to a fresh database.
  • CI job timeout-minutes: 30 vs go test -timeout 20m - the 10-minute headroom is reasonable given sequential container + two 150s TLS boots.

Test coverage

Coverage is the point of this PR and it is comprehensive: every spec scenario is exercised or explicitly, loudly deferred - no gaps silently absent. The keytool-availability CI guard (command -v keytool) is a nice touch to stop TLS scenarios silently skipping in CI.

Overall: solid, mergeable work. None of the above is blocking; item (1) is the only concrete code change I would suggest.

@robfrank
robfrank merged commit cdb4573 into main Jul 4, 2026
22 of 27 checks passed
@robfrank
robfrank deleted the feat/4887-e2e-go branch July 4, 2026 15:23
robfrank added a commit that referenced this pull request Aug 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bolt/e2e-go: new module using neo4j-go-driver

2 participants