@@ -223,6 +223,52 @@ func TestBulkLoadNodesCommaInProperties(t *testing.T) {
223223 }
224224}
225225
226+ // TestBulkLoadEdgesPipeInTargetID is a regression test for Istio-style IDs
227+ // that contain the field delimiter '|' literally (e.g. EDS cluster names
228+ // "inbound|7070|tcplocal|s1tcp.none" parsed from JSON config). Go's csv.Writer
229+ // RFC-4180-wraps such fields in '"', but Kuzu's default ESCAPE is backslash
230+ // not doubled-quote — so without explicit QUOTE='"', ESCAPE='"' the COPY
231+ // FROM splits the wrapped field on each interior '|' and aborts with
232+ // "expected N values per row, but got more". Fix: explicit QUOTE/ESCAPE in
233+ // the COPY FROM clause.
234+ func TestBulkLoadEdgesPipeInTargetID (t * testing.T ) {
235+ s , err := graph .Open (filepath .Join (t .TempDir (), "g.kuzu" ))
236+ if err != nil {
237+ t .Fatal (err )
238+ }
239+ defer s .Close ()
240+ if err := s .ApplySchema (); err != nil {
241+ t .Fatal (err )
242+ }
243+ // Istio-flavoured target ID with literal pipes.
244+ target := "json:istio/none_cds.json:inbound|7070|tcplocal|s1tcp.none"
245+ nodes := []* model.CodeNode {
246+ {ID : "json:istio/none_cds.json" , Kind : model .NodeModule , Label : "none_cds.json" },
247+ {ID : target , Kind : model .NodeConfigKey , Label : "inbound|7070|tcplocal|s1tcp.none" },
248+ }
249+ if err := s .BulkLoadNodes (nodes ); err != nil {
250+ t .Fatalf ("BulkLoadNodes with pipe-bearing ID: %v" , err )
251+ }
252+ edges := []* model.CodeEdge {{
253+ ID : "json:istio/none_cds.json->" + target ,
254+ Kind : model .EdgeContains ,
255+ SourceID : "json:istio/none_cds.json" ,
256+ TargetID : target ,
257+ Confidence : model .ConfidenceSyntactic ,
258+ Source : "JsonStructureDetector" ,
259+ }}
260+ if err := s .BulkLoadEdges (edges ); err != nil {
261+ t .Fatalf ("BulkLoadEdges with pipe-bearing target ID: %v" , err )
262+ }
263+ rows , err := s .Cypher ("MATCH ()-[r:CONTAINS]->() RETURN r.id AS id" )
264+ if err != nil {
265+ t .Fatal (err )
266+ }
267+ if len (rows ) != 1 {
268+ t .Fatalf ("want 1 CONTAINS row, got %d: %v" , len (rows ), rows )
269+ }
270+ }
271+
226272// TestBulkLoadEdgesEmpty — zero edges is a no-op like the node path.
227273func TestBulkLoadEdgesEmpty (t * testing.T ) {
228274 s , err := graph .Open (filepath .Join (t .TempDir (), "g.kuzu" ))
0 commit comments