Files
wehub-resource-sync a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:33:42 +08:00

162 lines
5.2 KiB
Go

package exporter
import (
"bytes"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/zzet/gortex/internal/graph"
)
func TestCypher_NodeShape(t *testing.T) {
g := buildSampleGraph(t)
var buf bytes.Buffer
_, err := WriteCypher(&buf, g, Options{})
require.NoError(t, err)
out := buf.String()
// Each node should appear as a labeled CREATE.
assert.Contains(t, out, "CREATE (:Function:GortexNode")
assert.Contains(t, out, "CREATE (:Method:GortexNode")
assert.Contains(t, out, "CREATE (:Field:GortexNode")
// Required props on every node.
assert.Contains(t, out, "id: 'main.go::F'")
assert.Contains(t, out, "name: 'F'")
assert.Contains(t, out, "qual_name: 'example.com/m.F'")
assert.Contains(t, out, "file_path: 'main.go'")
assert.Contains(t, out, "start_line: 3")
assert.Contains(t, out, "language: 'go'")
assert.Contains(t, out, "repo_prefix: 'example'")
// Meta keys flattened onto the node, JSON for nested values.
assert.Contains(t, out, "visibility: 'public'")
assert.Contains(t, out, "complexity: 4")
assert.Contains(t, out, `tags: '["hot","core"]'`)
// Pure-statement format: no comment lines, no DDL. The first line of
// the output should be a CREATE statement, not a `//` header.
assert.NotContains(t, out, "//", "output must not contain comments — Memgraph's .cypherl loader rejects them")
assert.NotContains(t, out, "CREATE INDEX", "DDL syntax differs between Neo4j and Memgraph; keep the file portable")
assert.True(t, strings.HasPrefix(out, "CREATE (:"),
"first line should be a CREATE statement; got: %q", out[:min(60, len(out))])
}
func TestCypher_EdgeShape(t *testing.T) {
g := buildSampleGraph(t)
var buf bytes.Buffer
_, err := WriteCypher(&buf, g, Options{})
require.NoError(t, err)
out := buf.String()
assert.Contains(t, out, "MATCH (a:GortexNode {id: 'main.go::F'}), (b:GortexNode {id: 'lib.go::Logger.Print'}) CREATE (a)-[:CALLS")
assert.Contains(t, out, "confidence: 1")
assert.Contains(t, out, "confidence_label: 'EXTRACTED'")
assert.Contains(t, out, "origin: 'ast_resolved'")
assert.Contains(t, out, "line: 4")
// Writes edge with meta.
assert.Contains(t, out, ":WRITES")
assert.Contains(t, out, "receiver_type: 'Logger'")
assert.Contains(t, out, "origin: 'ast_inferred'")
}
func TestCypher_StringEscaping(t *testing.T) {
g := graph.New()
g.AddNode(&graph.Node{
ID: "x.go::Quote", Kind: graph.KindFunction, Name: "Quote",
FilePath: "x.go", StartLine: 1, EndLine: 1, Language: "go",
Meta: map[string]any{
"doc": "It's a \"thing\"\n\twith escapes.",
},
})
var buf bytes.Buffer
_, err := WriteCypher(&buf, g, Options{})
require.NoError(t, err)
out := buf.String()
// Single-quote should be backslash-escaped.
assert.Contains(t, out, `\'`)
// Newline should be \n literal in the source, not an actual newline inside the string.
assert.Contains(t, out, `\n`)
assert.Contains(t, out, `\t`)
}
func TestCypher_StatsCount(t *testing.T) {
g := buildSampleGraph(t)
var buf bytes.Buffer
stats, err := WriteCypher(&buf, g, Options{})
require.NoError(t, err)
assert.Equal(t, 3, stats.NodesWritten)
assert.Equal(t, 2, stats.EdgesWritten)
assert.Equal(t, int64(buf.Len()), stats.BytesWritten)
}
func TestCypher_EmptyGraph(t *testing.T) {
g := graph.New()
var buf bytes.Buffer
stats, err := WriteCypher(&buf, g, Options{})
require.NoError(t, err)
assert.Equal(t, 0, stats.NodesWritten)
assert.Equal(t, 0, stats.EdgesWritten)
// Empty graph produces an empty file. .cypherl loaders accept this;
// any header / DDL is the CLI's responsibility (printed to stderr).
assert.Empty(t, buf.String())
}
func TestCypher_FilterRemovesDanglingEdges(t *testing.T) {
g := buildSampleGraph(t)
var buf bytes.Buffer
_, err := WriteCypher(&buf, g, Options{Kinds: []graph.NodeKind{graph.KindFunction}})
require.NoError(t, err)
out := buf.String()
assert.Contains(t, out, "CREATE (:Function:GortexNode")
assert.NotContains(t, out, "CREATE (:Method:GortexNode")
assert.NotContains(t, out, ":CALLS",
"edges with excluded endpoints must be dropped, not produce dangling MATCH")
}
func TestCypher_PropertyNameSanitization(t *testing.T) {
g := graph.New()
g.AddNode(&graph.Node{
ID: "x.go::F", Kind: graph.KindFunction, Name: "F",
FilePath: "x.go", StartLine: 1, EndLine: 1, Language: "go",
Meta: map[string]any{
"with-dash": 1,
"with space": 2,
"normal": 3,
"@@@invalid": 4, // will sanitize to "___invalid"
"meta.dotted": 5,
},
})
var buf bytes.Buffer
_, err := WriteCypher(&buf, g, Options{})
require.NoError(t, err)
out := buf.String()
assert.Contains(t, out, "with_dash: 1")
assert.Contains(t, out, "with_space: 2")
assert.Contains(t, out, "normal: 3")
assert.Contains(t, out, "meta_dotted: 5")
// Hyphen / space / dot should never appear inside property keys.
for _, line := range strings.Split(out, "\n") {
// We only care about the property-map portion of CREATE statements.
if !strings.Contains(line, "CREATE (:") {
continue
}
// A key always looks like ` <ident>:`. None of our sanitized keys
// should contain a non-identifier character.
assert.NotContains(t, line, " with-dash:")
assert.NotContains(t, line, " with space:")
assert.NotContains(t, line, " meta.dotted:")
}
}