Documentation
¶
Overview ¶
Package tck records the conformance evolution of the GoGraph Cypher engine against the openCypher Technology Compatibility Kit.
Conformance History ¶
Parser-level pass rate (grammar + AST round-trip):
Sprint 26: ~70% — baseline after initial grammar implementation
Sprint 27: ~72% — write-clause parser support
Sprint 28: ~74% — expression improvements, MATCH patterns
Sprint 29: 76.5% → 90.7% — normalizeSingleQuotes pre-processor resolved 579
single-quote-string scenarios; DDL/procedure scenarios added
Sprint 30: 90.7% — Bolt server; no parser changes
Sprint 31: 90.7% — godog execution runner added; parser rate unchanged;
execution-level rate baseline = 10.4% (407/3897 scenarios)
Sprint 43: 100.0% — task #402 closed the last grammar-gap-literal sub-class;
parser is fully green (3897/3897)
Execution-level pass rate (full godog runner, tckExecutionBaseline gate):
Sprint 31: 10.4% (407/3897) — baseline Sprint 37: 24.8% (968/3897) Sprint 42: 29.6% (1152/3897) Sprint 46: 39.4% (1536/3897) Sprints 58–64 (rounds 22–64, 2026-05-28/29): 100.0% (3897/3897) — FULLY GREEN Key uplifts: error-step regex (R58), VLE cross-pattern no-repeat-rel (R59+R61), PatternComprehension + percentile guard (R60), CREATE-multiplicity counter (R62), per-CREATE-instance edge labels + multigraph adjlist (R63), named-path leading-hop reconstruction (R64).
The enforced gate is const tckExecutionBaseline = 3897 in runner_test.go. Any PR that lowers the passing count is rejected by CI. See docs/tck/DIVERGENCES.md for the full audit trail.
Package tck implements the openCypher Technology Compatibility Kit (TCK) parser-only scenario runner. It reads the vendored Gherkin feature files from the embedded [features] directory, extracts every "When executing query:" step, and runs each Cypher string through [parser.Parse].
Scope ¶
The runner covers all 220 feature files from the openCypher TCK corpus (opencypher/openCypher@main, retrieved 2026-05-20). Raw scenario count before expansion is 1 615. After [parseFeatureFile] expands Scenario Outline blocks by substituting each Examples table row, the effective corpus grows to 3 897 scenarios. Of these, 914 are excluded from the pass-rate gate because they exercise grammar features not yet supported by the antlr/grammars-v4 grammar pinned at commit 284602b. See SkipReason for the full taxonomy.
The 2 983 remaining scenarios must pass at 100 %. A regression drops the pass rate below 100 % and causes [TestTCKParserOnly] to fail, blocking CI.
Concurrency ¶
[TestTCKParserOnly] is a standard Go test. It may be run under -race without additional synchronisation because every scenario invokes [parser.Parse] in its own goroutine via t.Run, and parser.Parse is documented as concurrency-safe.
Feature file provenance ¶
Feature files under features/ are vendored from:
https://github.com/opencypher/openCypher/tree/main/tck/features
Licensed under the Apache License, Version 2.0. See individual file headers.
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var WriteFeatureDirs = []string{
"features/clauses/create",
"features/clauses/merge",
"features/clauses/delete",
"features/clauses/set",
"features/clauses/remove",
}
WriteFeatureDirs lists the TCK feature directories that contain write-clause scenarios.
Functions ¶
func FeatureFiles ¶
FeatureFiles returns the embedded openCypher TCK feature file tree. It is exposed for use by the external test package (tck_test) so that the godog runner can load feature files from the embedded FS without importing godog in non-test production code.
Types ¶
type DDLScenario ¶
type DDLScenario struct {
// Name is a short identifier for the scenario, used as the sub-test name.
Name string
// Query is the Cypher statement to execute.
Query string
// WantErr reports whether the scenario expects an error from the engine.
WantErr bool
// ErrContains is a substring expected in the error message when WantErr is
// true. An empty string means any non-nil error satisfies the expectation.
ErrContains string
}
DDLScenario represents one engine-level integration scenario for DDL/procedure/parameter testing.
func DDLScenarios ¶
func DDLScenarios() []DDLScenario
DDLScenarios returns the set of DDL/procedure/parameter integration scenarios. These test the execution engine directly, not just parsing. They cover extensions added in Sprint 29 (index DDL, constraint DDL, parameters, and built-in procedure calls).
Notes on supported DDL syntax:
CREATE INDEX: the engine's DDL parser requires the optional IF NOT EXISTS clause to appear before the index name, i.e.: CREATE INDEX [IF NOT EXISTS] [name] FOR (n:Label) ON (n.prop)
CREATE CONSTRAINT: the engine uses the pre-4.x ASSERT syntax, i.e.: CREATE CONSTRAINT [name] ON (n:Label) ASSERT n.prop IS UNIQUE
Parameters in standalone RETURN require a driving clause. Use MATCH (n) RETURN $param to ensure the param is resolved at execution time.
type Scenario ¶
type Scenario struct {
// File is the path of the feature file relative to the embed root, e.g.
// "features/clauses/return/Return1.feature".
File string
// Feature is the Feature: header from the containing feature file.
Feature string
// Name is the Scenario: text, including its [N] index prefix where present.
Name string
// Tags are the @tag annotations on the Scenario block.
Tags []string
// Query is the Cypher string from the "When executing query:" step.
Query string
// SyntaxErrorType is non-empty when the scenario expects a SyntaxError,
// e.g. "UndefinedVariable", "UnexpectedSyntax".
SyntaxErrorType string
// SkipReason is non-empty when the scenario is excluded from the pass-rate
// gate. It records the grammar-gap category that caused the exclusion.
SkipReason SkipReason
}
Scenario represents a single Gherkin Scenario extracted from a feature file. Fields are exported so that test code in external test packages can read them.
Example ¶
ExampleScenario shows how the harness consumer turns a scenario's Query into an AST: this is the core operation the parser-only runner performs for every non-skipped scenario. Here a representative query is parsed directly so the example stays deterministic and independent of corpus ordering.
package main
import (
"fmt"
"github.com/FlavioCFOliveira/GoGraph/cypher/parser"
"github.com/FlavioCFOliveira/GoGraph/cypher/tck"
)
func main() {
// A Scenario carries the Cypher string lifted from its
// "When executing query:" step; feed that to parser.Parse.
s := &tck.Scenario{
File: "features/clauses/return/Return1.feature",
Name: "[1] Return a single value",
Query: "RETURN 1 AS one",
}
_, err := parser.Parse(s.Query)
fmt.Println("parses cleanly:", err == nil)
}
Output: parses cleanly: true
func LoadScenarios ¶
LoadScenarios walks the embedded feature directory and parses all Gherkin files, returning every Scenario that contains a "When executing query:" step. Each returned Scenario has its SkipReason field set.
LoadScenarios is safe to call concurrently.
Example ¶
ExampleLoadScenarios loads the embedded TCK corpus and inspects one scenario. The corpus content is fixed at build time, so "the corpus is non-empty" and "each scenario carries a query and a file path" are stable facts to assert; the exact scenario count is intentionally not pinned here.
package main
import (
"fmt"
"github.com/FlavioCFOliveira/GoGraph/cypher/tck"
)
func main() {
scenarios, err := tck.LoadScenarios()
if err != nil {
fmt.Println("load error:", err)
return
}
fmt.Println("corpus non-empty:", len(scenarios) > 0)
// Every scenario exposes the query string and originating feature file.
first := scenarios[0]
fmt.Println("has query:", first.Query != "")
fmt.Println("has file:", first.File != "")
}
Output: corpus non-empty: true has query: true has file: true
func LoadWriteScenarios ¶
LoadWriteScenarios returns all scenarios from write-clause feature files. It is a filtered view of LoadScenarios, restricted to the directories listed in WriteFeatureDirs. Each returned Scenario has its SkipReason field set by [classifySkip], including scenarios expanded from Scenario Outline blocks.
LoadWriteScenarios is safe to call concurrently.
func (*Scenario) WantParseError ¶
WantParseError reports whether the scenario expects [parser.Parse] to return a non-nil error.
type SkipReason ¶
type SkipReason string
SkipReason categorises why a scenario is excluded from the pass-rate gate. Every category corresponds to a known gap between the antlr/grammars-v4 grammar (commit 284602b) and the full openCypher specification.
const ( // SkipNone means the scenario is included in the pass-rate gate. SkipNone SkipReason = "" // SkipPlaceholder excludes Scenario Outline template rows that contain // angle-bracket placeholders (<pattern>, <yield>, etc.) which are not // valid Cypher. SkipPlaceholder SkipReason = "placeholder-template" // SkipSingleQuoteString excludes queries with multi-word single-quoted // string literals (e.g. 'The Matrix'). The grammar tokenises them as a // char literal followed by an identifier, producing a spurious parse error. SkipSingleQuoteString SkipReason = "single-quote-string" // SkipVarlenExplicitBound is retained for reference. The skip condition was // resolved via normalizeVarlenBounds in cypher/parser/normalize.go, // which pre-processes unsigned integer range bounds into their negated form so // that the ANTLR lexer emits DIGIT tokens instead of ID tokens. // //nolint:unused // retained for documentation; normalizeVarlenBounds resolves the gap SkipVarlenExplicitBound SkipReason = "varlen-explicit-bound" // SkipVarlenDotDot is retained for documentation. The skip condition was // removed because normalizeVarlenDotDot in cypher/parser/normalize.go // pre-processes [..], [N..], [..M], [N..M] patterns (without *) to their // equivalent [*..], [*N..], [*..M], [*N..M] forms before lexing. // //nolint:unused // retained for documentation; normalizeVarlenDotDot resolves the gap SkipVarlenDotDot SkipReason = "varlen-dotdot" // SkipChainedWith is retained for documentation. The skip condition was // removed because MultiPartQ() in the generated parser was modified to // consume readingStatement* segments interleaved with each WITH clause, // enabling unlimited chaining of WITH clauses in a query. // //nolint:unused // retained for documentation; MultiPartQ modification resolves the gap SkipChainedWith SkipReason = "chained-with" // SkipNegHexOct is retained for documentation. The skip condition was // removed because normalizeNegHexOct in cypher/parser/normalize.go // rewrites -0x… and -0o… literals to (0-0x…) / (0-0o…) before lexing, // which the grammar accepts as a binary subtraction expression. // //nolint:unused // retained for documentation; normalizeNegHexOct resolves the gap SkipNegHexOct SkipReason = "neg-hex-oct" // SkipLeadingDotFloat is retained for documentation. The skip condition was // removed because .5, -.5, and similar leading-dot floats already parse // correctly via the ANTLR grammar without preprocessing. The skip was // overly conservative. // //nolint:unused // retained for documentation; leading-dot floats parse correctly without a skip SkipLeadingDotFloat SkipReason = "leading-dot-float" // SkipZeroDotFloat is retained for documentation. The skip condition was // removed because normalizeZeroDotFloat in cypher/parser/normalize.go // pre-processes 0.NNN literals to .NNN before lexing, resolving the gap. // //nolint:unused // retained for documentation; normalizeZeroDotFloat resolves the gap SkipZeroDotFloat SkipReason = "zero-dot-float" // SkipDoubleNot is retained for documentation. The skip condition was // removed because normalizeDoubleNot in cypher/parser/normalize.go // applies double-negation elimination (NOT NOT expr → expr, NOT NOT NOT expr // → NOT expr) before lexing, resolving the grammar gap. // //nolint:unused // retained for documentation; normalizeDoubleNot resolves the gap SkipDoubleNot SkipReason = "double-not" // SkipCallNoParen is retained for documentation. The skip condition was // removed because QueryCallSt() in the generated parser was modified to // make the argument parentheses optional for in-query CALL, matching the // behaviour of StandaloneCall. // //nolint:unused // retained for documentation; QueryCallSt modification resolves the gap SkipCallNoParen SkipReason = "call-no-paren" // SkipOverflowAsSema is retained for documentation. The skip condition was // removed because IntegerOverflow and FloatingPointOverflow are now listed in // parseTimeErrors: the visitor returns a non-nil error for overflow, which // satisfies the TCK expectation of a compile-time SyntaxError. // //nolint:unused // retained for documentation; overflow scenarios now handled via parseTimeErrors SkipOverflowAsSema SkipReason = "overflow-as-sema" // SkipGrammarGapLiteral excludes specific literal scenarios where the // grammar is more permissive than the specification: // // - InvalidUnicodeLiteral / InvalidUnicodeCharacter: the grammar does not // validate unicode escape sequences or disallow non-ASCII operator // characters. // - InvalidNumberLiteral: the grammar tokenises malformed hex/integer // literals as two valid tokens rather than a single error token. // - UnexpectedSyntax on map keys starting with a digit: the grammar // permits them. // - UnexpectedSyntax on pattern expressions in RETURN/WITH/SET: the // grammar accepts them; the restriction is semantic. SkipGrammarGapLiteral SkipReason = "grammar-gap-literal" // SkipLongFloatSema is retained for documentation. The skip condition was // removed because strconv.ParseFloat handles very long but finite decimal // float literals correctly, rounding to the nearest IEEE-754 double without // error; the visitor no longer raises a SemaError for such literals. // //nolint:unused // retained for documentation; long valid floats parse correctly without a skip SkipLongFloatSema SkipReason = "long-float-sema" )