Skip to main content
Glama
orneryd

M.I.M.I.R - Multi-agent Intelligent Memory & Insight Repository

by orneryd
string_patterns_test.go13 kB
package cypher import ( "testing" ) func TestSplitByKeyword(t *testing.T) { tests := []struct { name string input string keyword string expected []string }{ { name: "single MATCH", input: "MATCH (n) RETURN n", keyword: "MATCH", expected: []string{"", "(n) RETURN n"}, }, { name: "multiple MATCH", input: "MATCH (a) MATCH (b) RETURN a, b", keyword: "MATCH", expected: []string{"", "(a) ", "(b) RETURN a, b"}, }, { name: "lowercase match", input: "match (n) return n", keyword: "MATCH", expected: []string{"", "(n) return n"}, }, { name: "no keyword", input: "CREATE (n) RETURN n", keyword: "MATCH", expected: []string{"CREATE (n) RETURN n"}, }, { name: "empty string", input: "", keyword: "MATCH", expected: []string{""}, }, { name: "keyword at end without space", input: "test MATCH", keyword: "MATCH", expected: []string{"test MATCH"}, }, { name: "CREATE split", input: "CREATE (a) CREATE (b)", keyword: "CREATE", expected: []string{"", "(a) ", "(b)"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := SplitByKeyword(tt.input, tt.keyword) if len(result) != len(tt.expected) { t.Errorf("SplitByKeyword(%q, %q) = %v (len=%d), want %v (len=%d)", tt.input, tt.keyword, result, len(result), tt.expected, len(tt.expected)) return } for i := range result { if result[i] != tt.expected[i] { t.Errorf("SplitByKeyword(%q, %q)[%d] = %q, want %q", tt.input, tt.keyword, i, result[i], tt.expected[i]) } } }) } } func TestSplitByMatch(t *testing.T) { result := SplitByMatch("MATCH (a) MATCH (b)") if len(result) != 3 { t.Errorf("SplitByMatch returned %d parts, want 3", len(result)) } } func TestSplitByCreate(t *testing.T) { result := SplitByCreate("CREATE (a) CREATE (b)") if len(result) != 3 { t.Errorf("SplitByCreate returned %d parts, want 3", len(result)) } } func TestExtractLimit(t *testing.T) { tests := []struct { name string input string expected int found bool }{ { name: "simple limit", input: "MATCH (n) RETURN n LIMIT 10", expected: 10, found: true, }, { name: "limit with skip", input: "MATCH (n) RETURN n SKIP 5 LIMIT 20", expected: 20, found: true, }, { name: "lowercase limit", input: "MATCH (n) RETURN n limit 15", expected: 15, found: true, }, { name: "no limit", input: "MATCH (n) RETURN n", expected: 0, found: false, }, { name: "limit with extra spaces", input: "MATCH (n) RETURN n LIMIT 25", expected: 25, found: true, }, { name: "large limit", input: "MATCH (n) RETURN n LIMIT 1000000", expected: 1000000, found: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result, found := ExtractLimit(tt.input) if found != tt.found { t.Errorf("ExtractLimit(%q) found=%v, want found=%v", tt.input, found, tt.found) } if result != tt.expected { t.Errorf("ExtractLimit(%q) = %d, want %d", tt.input, result, tt.expected) } }) } } func TestExtractSkip(t *testing.T) { tests := []struct { name string input string expected int found bool }{ { name: "simple skip", input: "MATCH (n) RETURN n SKIP 5", expected: 5, found: true, }, { name: "skip with limit", input: "MATCH (n) RETURN n SKIP 10 LIMIT 20", expected: 10, found: true, }, { name: "no skip", input: "MATCH (n) RETURN n LIMIT 10", expected: 0, found: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result, found := ExtractSkip(tt.input) if found != tt.found { t.Errorf("ExtractSkip(%q) found=%v, want found=%v", tt.input, found, tt.found) } if result != tt.expected { t.Errorf("ExtractSkip(%q) = %d, want %d", tt.input, result, tt.expected) } }) } } func TestExtractLimitString(t *testing.T) { result := ExtractLimitString("MATCH (n) RETURN n LIMIT 42") if result != "42" { t.Errorf("ExtractLimitString returned %q, want %q", result, "42") } result = ExtractLimitString("MATCH (n) RETURN n") if result != "" { t.Errorf("ExtractLimitString returned %q, want empty string", result) } } func TestFindKeywordIndexStringPatterns(t *testing.T) { tests := []struct { name string input string keyword string expected int }{ { name: "find MATCH", input: "MATCH (n) RETURN n", keyword: "MATCH", expected: 0, }, { name: "find CREATE mid-string", input: "MATCH (n) CREATE (m)", keyword: "CREATE", expected: 10, }, { name: "case insensitive", input: "match (n) return n", keyword: "MATCH", expected: 0, }, { name: "not found", input: "MATCH (n) RETURN n", keyword: "DELETE", expected: -1, }, { name: "word boundary - not partial match", input: "CREATEMATCH (n)", keyword: "MATCH", expected: -1, // MATCH is part of CREATEMATCH, not a word }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := FindKeywordIndex(tt.input, tt.keyword) if result != tt.expected { t.Errorf("FindKeywordIndex(%q, %q) = %d, want %d", tt.input, tt.keyword, result, tt.expected) } }) } } func TestContainsKeyword(t *testing.T) { if !ContainsKeyword("MATCH (n) RETURN n", "MATCH") { t.Error("ContainsKeyword should find MATCH") } if ContainsKeyword("CREATE (n)", "MATCH") { t.Error("ContainsKeyword should not find MATCH in CREATE query") } } // ============================================================================= // Phase 2: Aggregation and Parameter Tests // ============================================================================= func TestParseAggregation(t *testing.T) { tests := []struct { name string input string expected *AggregationResult }{ { name: "COUNT with property", input: "COUNT(n.age)", expected: &AggregationResult{ Function: "COUNT", Variable: "n", Property: "age", }, }, { name: "SUM with property", input: "SUM(x.value)", expected: &AggregationResult{ Function: "SUM", Variable: "x", Property: "value", }, }, { name: "COUNT star", input: "COUNT(*)", expected: &AggregationResult{ Function: "COUNT", IsStar: true, }, }, { name: "COUNT variable only", input: "COUNT(n)", expected: &AggregationResult{ Function: "COUNT", Variable: "n", }, }, { name: "AVG with distinct", input: "AVG(DISTINCT n.price)", expected: &AggregationResult{ Function: "AVG", Variable: "n", Property: "price", Distinct: true, }, }, { name: "COLLECT with property", input: "COLLECT(p.name)", expected: &AggregationResult{ Function: "COLLECT", Variable: "p", Property: "name", }, }, { name: "not an aggregation", input: "n.name", expected: nil, }, { name: "empty string", input: "", expected: nil, }, { name: "lowercase function", input: "sum(n.value)", expected: &AggregationResult{ Function: "SUM", Variable: "n", Property: "value", }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := ParseAggregation(tt.input) if tt.expected == nil { if result != nil { t.Errorf("ParseAggregation(%q) = %+v, want nil", tt.input, result) } return } if result == nil { t.Errorf("ParseAggregation(%q) = nil, want %+v", tt.input, tt.expected) return } if result.Function != tt.expected.Function { t.Errorf("Function = %q, want %q", result.Function, tt.expected.Function) } if result.Variable != tt.expected.Variable { t.Errorf("Variable = %q, want %q", result.Variable, tt.expected.Variable) } if result.Property != tt.expected.Property { t.Errorf("Property = %q, want %q", result.Property, tt.expected.Property) } if result.Distinct != tt.expected.Distinct { t.Errorf("Distinct = %v, want %v", result.Distinct, tt.expected.Distinct) } if result.IsStar != tt.expected.IsStar { t.Errorf("IsStar = %v, want %v", result.IsStar, tt.expected.IsStar) } }) } } func TestParseAggregationProperty(t *testing.T) { v, p := ParseAggregationProperty("SUM(n.price)") if v != "n" || p != "price" { t.Errorf("got (%q, %q), want (n, price)", v, p) } v, p = ParseAggregationProperty("not_agg") if v != "" || p != "" { t.Errorf("got (%q, %q), want empty strings", v, p) } } func TestExtractParameters(t *testing.T) { tests := []struct { name string input string expected []string }{ { name: "single parameter", input: "WHERE n.name = $name", expected: []string{"name"}, }, { name: "multiple parameters", input: "WHERE n.name = $name AND n.age > $minAge", expected: []string{"name", "minAge"}, }, { name: "no parameters", input: "MATCH (n) RETURN n", expected: nil, }, { name: "parameter with underscore", input: "WHERE n.id = $user_id", expected: []string{"user_id"}, }, { name: "parameter with numbers", input: "WHERE n.id = $id123", expected: []string{"id123"}, }, { name: "invalid parameter (number first)", input: "WHERE n.id = $123abc", expected: nil, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := ExtractParameters(tt.input) if len(result) != len(tt.expected) { t.Errorf("ExtractParameters(%q) = %v, want %v", tt.input, result, tt.expected) return } for i := range result { if result[i] != tt.expected[i] { t.Errorf("result[%d] = %q, want %q", i, result[i], tt.expected[i]) } } }) } } func TestReplaceParameters(t *testing.T) { tests := []struct { name string input string expected string }{ { name: "single replacement", input: "WHERE n.name = $name", expected: "WHERE n.name = 'Alice'", }, { name: "multiple replacements", input: "WHERE n.name = $name AND n.age = $age", expected: "WHERE n.name = 'Alice' AND n.age = '30'", }, { name: "no parameters", input: "MATCH (n) RETURN n", expected: "MATCH (n) RETURN n", }, { name: "unknown parameter kept", input: "WHERE n.x = $unknown", expected: "WHERE n.x = $unknown", }, } replacer := func(param string) string { switch param { case "name": return "'Alice'" case "age": return "'30'" default: return "$" + param } } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := ReplaceParameters(tt.input, replacer) if result != tt.expected { t.Errorf("ReplaceParameters(%q) = %q, want %q", tt.input, result, tt.expected) } }) } } // ============================================================================= // Benchmarks to verify performance improvements // ============================================================================= func BenchmarkSplitByKeyword(b *testing.B) { query := "MATCH (a:Person) MATCH (b:Person) WHERE a.name = 'Alice' RETURN a, b" b.ResetTimer() for i := 0; i < b.N; i++ { SplitByKeyword(query, "MATCH") } } func BenchmarkSplitByKeywordRegex(b *testing.B) { query := "MATCH (a:Person) MATCH (b:Person) WHERE a.name = 'Alice' RETURN a, b" b.ResetTimer() for i := 0; i < b.N; i++ { matchKeywordPattern.Split(query, -1) } } func BenchmarkExtractLimit(b *testing.B) { query := "MATCH (n:Person) WHERE n.age > 30 RETURN n LIMIT 100" b.ResetTimer() for i := 0; i < b.N; i++ { ExtractLimit(query) } } func BenchmarkExtractLimitRegex(b *testing.B) { query := "MATCH (n:Person) WHERE n.age > 30 RETURN n LIMIT 100" b.ResetTimer() for i := 0; i < b.N; i++ { limitPattern.FindStringSubmatch(query) } } // Phase 2 Benchmarks func BenchmarkParseAggregation(b *testing.B) { expr := "SUM(n.price)" b.ResetTimer() for i := 0; i < b.N; i++ { ParseAggregation(expr) } } func BenchmarkParseAggregationRegex(b *testing.B) { expr := "SUM(n.price)" b.ResetTimer() for i := 0; i < b.N; i++ { sumPropPattern.FindStringSubmatch(expr) } } func BenchmarkReplaceParameters(b *testing.B) { query := "MATCH (n:Person) WHERE n.name = $name AND n.age > $minAge RETURN n" replacer := func(param string) string { return "'" + param + "_value'" } b.ResetTimer() for i := 0; i < b.N; i++ { ReplaceParameters(query, replacer) } } func BenchmarkReplaceParametersRegex(b *testing.B) { query := "MATCH (n:Person) WHERE n.name = $name AND n.age > $minAge RETURN n" b.ResetTimer() for i := 0; i < b.N; i++ { parameterPattern.ReplaceAllStringFunc(query, func(match string) string { return "'" + match[1:] + "_value'" }) } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/orneryd/Mimir'

If you have feedback or need assistance with the MCP directory API, please join our Discord server