diff --git a/pkg/graph/parser.go b/pkg/graph/parser.go index c05130b..5e81a1f 100644 --- a/pkg/graph/parser.go +++ b/pkg/graph/parser.go @@ -36,8 +36,8 @@ type Query struct { var ( simpleLexer = lexer.MustSimple([]lexer.SimpleRule{ - {"Operator", `\b(?:and|or|xor)\b`}, // Prioritize operators - {"Ident", `[a-zA-Z][a-zA-Z0-9:/._@-]*`}, // Updated to handle colons, slashes, dots, underscores, hyphens, and @ + {"Operator", `\b(?:and|or|xor)\b`}, // Prioritize operators + {"Ident", `[a-zA-Z][a-zA-Z0-9:/._@?=&+\-]*`}, // Updated to handle colons, slashes, dots, underscores, hyphens, and @ {"String", `"(?:\\.|[^"])*"`}, {"Whitespace", `[ \t\n\r]+`}, {"LBracket", `\[`}, diff --git a/pkg/graph/parser_test.go b/pkg/graph/parser_test.go index 2045300..e7b1c67 100644 --- a/pkg/graph/parser_test.go +++ b/pkg/graph/parser_test.go @@ -6,9 +6,12 @@ import ( "github.com/RoaringBitmap/roaring" ) +// TestParseAndExecute tests basic queries on simple mock data. +// We extend these tests with queries that include qualifiers (e.g., ?type=jar). func TestParseAndExecute(t *testing.T) { storage := NewMockStorage() + // Create some mock nodes. node1, err := AddNode(storage, "PACKAGE", nil, "pkg:generic/lib-A@1.0.0") if err != nil { t.Fatal(err) @@ -26,6 +29,14 @@ func TestParseAndExecute(t *testing.T) { t.Fatal(err) } + // Create a node with a query-string in its purl. + nodeCamel, err := AddNode(storage, "PACKAGE", nil, "pkg:maven/org.apache.camel.quarkus/camel-quarkus-cassandraql-deployment@3.18.0-SNAPSHOT?type=jar") + if err != nil { + t.Fatal(err) + } + + // Set up some dependencies. + // Node1 -> Node3 -> Node4 err = node1.SetDependency(storage, node3) if err != nil { t.Fatal(err) @@ -39,6 +50,13 @@ func TestParseAndExecute(t *testing.T) { t.Fatal(err) } + // Make nodeCamel depend on node4 to verify a dependency query with a qualifier in the PURL. + err = nodeCamel.SetDependency(storage, node4) + if err != nil { + t.Fatal(err) + } + + // Cache the results for quicker lookups. if err := Cache(storage); err != nil { t.Fatal(err) } @@ -53,13 +71,13 @@ func TestParseAndExecute(t *testing.T) { { name: "Simple dependents query", script: "dependents PACKAGE pkg:generic/dep1@1.0.0", - want: roaring.BitmapOf(1, 2, 3), + want: roaring.BitmapOf(node1.ID, node2.ID, node3.ID), defaultNodeName: "", }, { name: "Simple dependencies query", script: "dependencies PACKAGE pkg:generic/lib-A@1.0.0", - want: roaring.BitmapOf(1, 3, 4), + want: roaring.BitmapOf(node1.ID, node3.ID, node4.ID), defaultNodeName: "", }, { @@ -71,7 +89,7 @@ func TestParseAndExecute(t *testing.T) { { name: "Combine dependents and dependencies with OR", script: "dependents PACKAGE pkg:generic/dep1@1.0.0 or dependencies PACKAGE pkg:generic/dep1@1.0.0", - want: roaring.BitmapOf(1, 2, 3, 4), + want: roaring.BitmapOf(node1.ID, node2.ID, node3.ID, node4.ID), defaultNodeName: "", }, { @@ -81,11 +99,31 @@ func TestParseAndExecute(t *testing.T) { defaultNodeName: "", }, { - name: "Empty node name", + name: "Empty node name defaults to pkg:generic/lib-A@1.0.0", script: "dependents PACKAGE or dependencies PACKAGE", - want: roaring.BitmapOf(1, 3, 4), + want: roaring.BitmapOf(node1.ID, node3.ID, node4.ID), defaultNodeName: "pkg:generic/lib-A@1.0.0", }, + + // New tests exercising qualifiers in the PURL. + { + name: "Dependencies query with ?type=jar qualifier", + script: "dependencies PACKAGE pkg:maven/org.apache.camel.quarkus/camel-quarkus-cassandraql-deployment@3.18.0-SNAPSHOT?type=jar", + want: roaring.BitmapOf(nodeCamel.ID, node4.ID), + defaultNodeName: "", + }, + { + name: "Dependents query with ?type=jar qualifier", + script: "dependents PACKAGE pkg:maven/org.apache.camel.quarkus/camel-quarkus-cassandraql-deployment@3.18.0-SNAPSHOT?type=jar", + want: roaring.BitmapOf(nodeCamel.ID), + defaultNodeName: "", + }, + { + name: "Invalid query with qualifier", + script: "invalid PACKAGE pkg:maven/org.apache.camel.quarkus/camel-quarkus-cassandraql@3.18.0-SNAPSHOT?type=jar", + wantErr: true, + defaultNodeName: "", + }, } for _, tt := range tests { @@ -102,11 +140,13 @@ func TestParseAndExecute(t *testing.T) { if err != nil { t.Fatal(err) } + result, err := ParseAndExecute(tt.script, storage, tt.defaultNodeName, nodes, caches, true) if (err != nil) != tt.wantErr { t.Errorf("ParseAndExecute() error = %v, wantErr %v", err, tt.wantErr) return } + if !tt.wantErr && !result.Equals(tt.want) { t.Errorf("ParseAndExecute() got = %v, want %v", result, tt.want) }