From e8a70da7d4132190bc6739f89903926d7048f115 Mon Sep 17 00:00:00 2001 From: Adam Dickmeiss Date: Mon, 2 Feb 2026 22:35:59 +0100 Subject: [PATCH 1/3] builder_test: assert and check error string Reveals that many errors turns into "query requires a root clause". --- cqlbuilder/builder_test.go | 326 +++++++++++++------------------------ 1 file changed, 115 insertions(+), 211 deletions(-) diff --git a/cqlbuilder/builder_test.go b/cqlbuilder/builder_test.go index 6571767..b467cec 100644 --- a/cqlbuilder/builder_test.go +++ b/cqlbuilder/builder_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/indexdata/cql-go/cql" + "github.com/stretchr/testify/assert" ) func TestBuilderSearch(t *testing.T) { @@ -12,13 +13,8 @@ func TestBuilderSearch(t *testing.T) { Rel(cql.EQ). Term("hello"). Build() - if err != nil { - t.Fatalf("build failed: %v", err) - } - - if got, want := query.String(), "dc.title = hello"; got != want { - t.Fatalf("unexpected query: got %q want %q", got, want) - } + assert.NoError(t, err, "build failed") + assert.Equal(t, "dc.title = hello", query.String(), "unexpected query string") } func TestBuilderSearchMultiWord(t *testing.T) { @@ -27,13 +23,8 @@ func TestBuilderSearchMultiWord(t *testing.T) { Rel(cql.EQ). Term("hello world"). Build() - if err != nil { - t.Fatalf("build failed: %v", err) - } - - if got, want := query.String(), "dc.title = \"hello world\""; got != want { - t.Fatalf("unexpected query: got %q want %q", got, want) - } + assert.NoError(t, err, "build failed") + assert.Equal(t, "dc.title = \"hello world\"", query.String(), "unexpected query string") } func TestBuilderBooleanAnd(t *testing.T) { @@ -45,13 +36,8 @@ func TestBuilderBooleanAnd(t *testing.T) { Rel(cql.GE). Term("2"). Build() - if err != nil { - t.Fatalf("build failed: %v", err) - } - - if got, want := query.String(), "a = one and b >= 2"; got != want { - t.Fatalf("unexpected query: got %q want %q", got, want) - } + assert.NoError(t, err, "build failed") + assert.Equal(t, "a = one and b >= 2", query.String(), "unexpected query string") } func TestBuilderPrefixSortAndEscaping(t *testing.T) { @@ -61,14 +47,10 @@ func TestBuilderPrefixSortAndEscaping(t *testing.T) { Term("the \"little\" prince"). SortBy("dc.title", cql.IgnoreCase). Build() - if err != nil { - t.Fatalf("build failed: %v", err) - } + assert.NoError(t, err, "build failed") want := "> dc = \"http://purl.org/dc/elements/1.1/\" dc.title = \"the \\\"little\\\" prince\" sortBy dc.title/ignoreCase" - if got := query.String(); got != want { - t.Fatalf("unexpected query: got %q want %q", got, want) - } + assert.Equal(t, want, query.String(), "unexpected query string") } func TestBuilderSafe(t *testing.T) { @@ -76,14 +58,8 @@ func TestBuilderSafe(t *testing.T) { Search("title"). Term("a*b?c\\^d"). Build() - if err != nil { - t.Fatalf("build failed: %v", err) - } - - if got, want := query.String(), "title = a\\*b\\?c\\\\\\^d"; got != want { - t.Fatalf("unexpected query: got %q want %q", got, want) - } - + assert.NoError(t, err, "build failed") + assert.Equal(t, "title = a\\*b\\?c\\\\\\^d", query.String(), "unexpected query string") } func TestBuilderTermUnsafe(t *testing.T) { @@ -91,13 +67,8 @@ func TestBuilderTermUnsafe(t *testing.T) { Search("title"). TermUnsafe("a*b?c\\^d"). Build() - if err != nil { - t.Fatalf("build failed: %v", err) - } - - if got, want := query.String(), "title = a*b?c\\^d"; got != want { - t.Fatalf("unexpected query: got %q want %q", got, want) - } + assert.NoError(t, err, "build failed") + assert.Equal(t, "title = a*b?c\\^d", query.String(), "unexpected query string") } func TestBuilderTermUnsafeEmpty(t *testing.T) { @@ -105,29 +76,36 @@ func TestBuilderTermUnsafeEmpty(t *testing.T) { Search("title"). TermUnsafe(""). Build() - if err != nil { - t.Fatalf("build failed: %v", err) - } - - if got, want := query.String(), "title = \"\""; got != want { - t.Fatalf("unexpected query: got %q want %q", got, want) - } + assert.NoError(t, err, "build failed") + assert.Equal(t, "title = \"\"", query.String(), "unexpected query string") } func TestBuilderValidation(t *testing.T) { - if _, err := NewQuery().Search("a").Term("").Build(); err == nil { - t.Fatalf("expected error for empty term") - } + _, err := NewQuery().Search("a").Term("").Build() + assert.Error(t, err, "expected error for empty term") + assert.Equal(t, "query requires a root clause", err.Error()) - if _, err := NewQuery().Search("a").Rel("bogus").Term("x").Build(); err == nil { - t.Fatalf("expected error for invalid relation") - } + _, err = NewQuery().Search("a").Rel("bogus").Term("x").Build() + assert.Error(t, err, "expected error for invalid relation") + assert.Equal(t, "query requires a root clause", err.Error()) + + _, err = NewQuery().SortBy("").Build() + assert.Error(t, err, "expected error for empty sort index") + assert.Equal(t, "sort index must be non-empty", err.Error()) + _, err = NewQuery().Prefix("", "http://example.org/").Build() + assert.Error(t, err, "expected error for empty prefix") + assert.Equal(t, "query requires a root clause", err.Error()) +} + +func TestBuilderDuplicateRoot(t *testing.T) { qb := NewQuery() - _, _ = qb.Search("a").Term("x").Build() - if _, err := qb.Search("b").Term("y").Build(); err == nil { - t.Fatalf("expected error for duplicate root") - } + _, err := qb.Search("a").Term("x").Build() + assert.NoError(t, err, "unexpected error building first root clause") + + _, err = qb.Search("b").Term("y").Build() + assert.Error(t, err, "expected error for duplicate root") + assert.Equal(t, "query already has a root clause", err.Error()) } func TestBuilderAppendToExistingQuery(t *testing.T) { @@ -146,29 +124,17 @@ func TestBuilderAppendToExistingQuery(t *testing.T) { Search("author"). Term("alice"). Build() - if err != nil { - t.Fatalf("build failed: %v", err) - } - - if got, want := query.String(), "title = base and author = alice"; got != want { - t.Fatalf("unexpected query: got %q want %q", got, want) - } + assert.NoError(t, err, "build failed") + assert.Equal(t, "title = base and author = alice", query.String(), "unexpected query string") } func TestBuilderFromString(t *testing.T) { qb, err := NewQueryFromString("title = base") - if err != nil { - t.Fatalf("parse failed: %v", err) - } + assert.NoError(t, err, "parse failed") query, err := qb.And().Search("author").Term("alice").Build() - if err != nil { - t.Fatalf("build failed: %v", err) - } - - if got, want := query.String(), "title = base and author = alice"; got != want { - t.Fatalf("unexpected query: got %q want %q", got, want) - } + assert.NoError(t, err, "build failed") + assert.Equal(t, "title = base and author = alice", query.String(), "unexpected query string") } func TestBuilderGroupedClause(t *testing.T) { @@ -189,93 +155,71 @@ func TestBuilderGroupedClause(t *testing.T) { Term("b"). EndClause(). Build() - if err != nil { - t.Fatalf("build failed: %v", err) - } + assert.NoError(t, err, "build failed") //CQL is left associative so the stringifier skips left parentheses - if got, want := query.String(), "a = a or b = b and (d = d or b = b)"; got != want { - t.Fatalf("unexpected query: got %q want %q", got, want) - } + assert.Equal(t, "a = a or b = b and (d = d or b = b)", query.String(), "unexpected query string") } func TestBuilderFromStringInjectionSafe(t *testing.T) { qb, err := NewQueryFromString("title = base") - if err != nil { - t.Fatalf("parse failed: %v", err) - } - + assert.NoError(t, err, "build failed") query, err := qb.And().Search("author").Term("\" OR injected=true").Build() - if err != nil { - t.Fatalf("build failed: %v", err) - } - - if got, want := query.String(), "title = base and author = \"\\\" OR injected=true\""; got != want { - t.Fatalf("unexpected query: got %q want %q", got, want) - } + assert.NoError(t, err, "build failed") + assert.Equal(t, "title = base and author = \"\\\" OR injected=true\"", query.String(), "unexpected query string") } func TestBuilderFromStringInjectionUnsafe(t *testing.T) { qb, err := NewQueryFromString("title = base") - if err != nil { - t.Fatalf("parse failed: %v", err) - } + assert.NoError(t, err, "parse failed") query, err := qb.And().Search("author").TermUnsafe("\" OR injected=true").Build() - if err != nil { - t.Fatalf("build failed: %v", err) - } - - if got, want := query.String(), "title = base and author = \"\" OR injected=true\""; got != want { - t.Fatalf("unexpected query: got %q want %q", got, want) - } + assert.NoError(t, err, "build failed") + assert.Equal(t, "title = base and author = \"\" OR injected=true\"", query.String(), "unexpected query string") } func TestBuilderErrorsAndModifiers(t *testing.T) { - if _, err := NewQuery().Build(); err == nil { - t.Fatalf("expected error for missing root clause") - } + _, err := NewQuery().Build() + assert.Error(t, err, "expected error for missing root clause") + assert.Equal(t, "query requires a root clause", err.Error()) - if _, err := NewQuery().Prefix("p", "").Build(); err == nil { - t.Fatalf("expected error for empty prefix uri") - } + _, err = NewQuery().Prefix("p", "").Build() + assert.Error(t, err, "expected error for empty prefix uri") + assert.Equal(t, "prefix uri must be non-empty", err.Error()) - if _, err := NewQuery().SortBy("").Build(); err == nil { - t.Fatalf("expected error for empty sort index") - } + _, err = NewQuery().SortBy("").Build() + assert.Error(t, err, "expected error for empty sort index") + assert.Equal(t, "sort index must be non-empty", err.Error()) - if _, err := NewQuery(). + _, err = NewQuery(). Search("a"). Term("b"). SortBy("title", ""). - Build(); err == nil { - t.Fatalf("expected error for empty sort modifier name") - } + Build() + assert.Error(t, err, "expected error for empty sort modifier name") - if _, err := NewQuery(). + _, err = NewQuery(). Search("a"). Term("b"). SortByModifiers("title", cql.Modifier{Name: "", Value: "x"}). - Build(); err == nil { - t.Fatalf("expected error for empty modifier name") - } + Build() + assert.Error(t, err, "expected error for empty modifier name") - if _, err := NewQuery(). + _, err = NewQuery(). Search("a"). Term("b"). SortByModifiers("title", cql.Modifier{Name: "x", Relation: "bogus"}). - Build(); err == nil { - t.Fatalf("expected error for invalid modifier relation") - } + Build() + assert.Error(t, err, "expected error for invalid modifier relation") } func TestBuilderAppendErrors(t *testing.T) { - if _, err := NewQuery(). + _, err := NewQuery(). And(). Search("a"). Term("b"). - Build(); err == nil { - t.Fatalf("expected error when appending without root") - } + Build() + assert.Error(t, err, "cannot append boolean operator without existing root clause") + assert.Equal(t, "builder is missing query context", err.Error()) } func TestBuilderBeginClauseErrors(t *testing.T) { @@ -286,60 +230,57 @@ func TestBuilderBeginClauseErrors(t *testing.T) { Term("b"). EndClause(). Build() - if err != nil { - t.Fatalf("unexpected error building first root clause: %v", err) - } - + assert.NoError(t, err, "unexpected error building first root clause") _, err = qb. BeginClause(). Search("c"). Term("d"). EndClause(). Build() - if err == nil { - t.Fatalf("expected error when starting a second root clause") - } + assert.Error(t, err, "expected error when starting a second root clause") + assert.Equal(t, "query already has a root clause", err.Error()) } func TestBuilderJoinModifiersValidation(t *testing.T) { - if _, err := NewQuery(). + _, err := NewQuery(). Search("a"). Term("b"). And(). Mod(""). Search("c"). Term("d"). - Build(); err == nil { - t.Fatalf("expected error for empty boolean modifier name") - } + Build() + assert.Error(t, err, "expected error for empty boolean modifier name") + assert.Equal(t, "builder is missing query context", err.Error()) - if _, err := NewQuery(). + _, err = NewQuery(). Search("a"). Term("b"). And(). ModRel(cql.Distance, "bogus", "1"). Search("c"). Term("d"). - Build(); err == nil { - t.Fatalf("expected error for invalid boolean modifier relation") - } + Build() + + assert.Error(t, err, "expected error for invalid boolean modifier relation") + assert.Equal(t, "builder is missing query context", err.Error()) } func TestBuilderRelationValidation(t *testing.T) { - if _, err := NewQuery(). + _, err := NewQuery(). Search("a"). Rel("bogus"). Term("b"). - Build(); err == nil { - t.Fatalf("expected error for invalid relation") - } + Build() + assert.Error(t, err, "expected error for invalid relation") + assert.Equal(t, "query requires a root clause", err.Error()) } func TestBuilderEndClauseWithoutStart(t *testing.T) { expr := &ExprBuilder{} - if _, err := expr.EndClause().Build(); err == nil { - t.Fatalf("expected error for EndClause without BeginClause") - } + _, err := expr.EndClause().Build() + assert.Error(t, err, "expected error for EndClause without BeginClause") + assert.Equal(t, "builder is missing query context", err.Error()) } func TestBuilderMultiplePrefixes(t *testing.T) { @@ -349,14 +290,9 @@ func TestBuilderMultiplePrefixes(t *testing.T) { Search("dc.title"). Term("hello"). Build() - if err != nil { - t.Fatalf("build failed: %v", err) - } - + assert.NoError(t, err, "build failed") want := "> dc = \"http://purl.org/dc/elements/1.1/\" > bath = \"http://z3950.org/bath/2.0/\" dc.title = hello" - if got := query.String(); got != want { - t.Fatalf("unexpected query: got %q want %q", got, want) - } + assert.Equal(t, want, query.String(), "unexpected query string") } func TestBuilderSortByModifiersEscaping(t *testing.T) { @@ -365,13 +301,8 @@ func TestBuilderSortByModifiersEscaping(t *testing.T) { Term("hello"). SortByModifiers("title", cql.Modifier{Name: "locale", Relation: cql.EQ, Value: "en\"US"}). Build() - if err != nil { - t.Fatalf("build failed: %v", err) - } - - if got, want := query.String(), "title = hello sortBy title/locale=\"en\\\"US\""; got != want { - t.Fatalf("unexpected query: got %q want %q", got, want) - } + assert.NoError(t, err, "build failed") + assert.Equal(t, "title = hello sortBy title/locale=\"en\\\"US\"", query.String(), "unexpected query string") } func TestBuilderSortByModifiersDefaultRelation(t *testing.T) { @@ -380,13 +311,8 @@ func TestBuilderSortByModifiersDefaultRelation(t *testing.T) { Term("hello"). SortByModifiers("title", cql.Modifier{Name: "locale", Value: "en_US"}). Build() - if err != nil { - t.Fatalf("build failed: %v", err) - } - - if got, want := query.String(), "title = hello sortBy title/locale=en_US"; got != want { - t.Fatalf("unexpected query: got %q want %q", got, want) - } + assert.NoError(t, err, "build failed") + assert.Equal(t, "title = hello sortBy title/locale=en_US", query.String(), "unexpected query string") } func TestBuilderBeginClauseRightHand(t *testing.T) { @@ -402,13 +328,9 @@ func TestBuilderBeginClauseRightHand(t *testing.T) { Term("c"). EndClause(). Build() - if err != nil { - t.Fatalf("build failed: %v", err) - } - if got, want := query.String(), "a = a and (b = b or c = c)"; got != want { - t.Fatalf("unexpected query: got %q want %q", got, want) - } + assert.NoError(t, err, "build failed") + assert.Equal(t, "a = a and (b = b or c = c)", query.String(), "unexpected query string") } func TestBuilderSearchModifiers(t *testing.T) { @@ -419,31 +341,27 @@ func TestBuilderSearchModifiers(t *testing.T) { ModRel(cql.Locale, cql.EQ, "en\"US"). Term("hello"). Build() - if err != nil { - t.Fatalf("build failed: %v", err) - } - if got, want := query.String(), "title =/locale/locale=\"en\\\"US\" hello"; got != want { - t.Fatalf("unexpected query: got %q want %q", got, want) - } + assert.NoError(t, err, "build failed") + assert.Equal(t, "title =/locale/locale=\"en\\\"US\" hello", query.String(), "unexpected query string") } func TestBuilderSearchModifiersValidation(t *testing.T) { - if _, err := NewQuery(). + _, err := NewQuery(). Search("title"). Mod(""). Term("hello"). - Build(); err == nil { - t.Fatalf("expected error for empty search modifier name") - } + Build() + assert.Error(t, err, "expected error for empty search modifier name") + assert.Equal(t, "query requires a root clause", err.Error()) - if _, err := NewQuery(). + _, err = NewQuery(). Search("title"). ModRel(cql.Locale, "bogus", "en"). Term("hello"). - Build(); err == nil { - t.Fatalf("expected error for invalid search modifier relation") - } + Build() + assert.Error(t, err, "expected error for invalid search modifier relation") + assert.Equal(t, "query requires a root clause", err.Error()) } func TestBuilderSearchRelationDefaults(t *testing.T) { @@ -452,13 +370,8 @@ func TestBuilderSearchRelationDefaults(t *testing.T) { Rel(""). Term("hello"). Build() - if err != nil { - t.Fatalf("build failed: %v", err) - } - - if got, want := query.String(), "title = hello"; got != want { - t.Fatalf("unexpected query: got %q want %q", got, want) - } + assert.NoError(t, err, "build failed") + assert.Equal(t, "title = hello", query.String(), "unexpected query string") } func TestBuilderAppendInvalidOperator(t *testing.T) { @@ -472,20 +385,11 @@ func TestBuilderAppendInvalidOperator(t *testing.T) { op: "bogus", } query, err := jb.Search("c").Term("d").Build() - if err != nil { - t.Fatalf("unexpected error for invalid boolean operator: %v", err) - } - if got, want := query.String(), "a = b"; got != want { - t.Fatalf("unexpected query: got %q want %q", got, want) - } + assert.NoError(t, err, "unexpected error for invalid boolean operator") + assert.Equal(t, "a = b", query.String(), "unexpected query string") } func TestBuilderEscapeHelpers(t *testing.T) { - if got, want := EscapeSpecialChars("a\\b\"c"), "a\\\\b\\\"c"; got != want { - t.Fatalf("unexpected EscapeSpecialChars: got %q want %q", got, want) - } - - if got, want := EscapeMaskingChars("*?^"), "\\*\\?\\^"; got != want { - t.Fatalf("unexpected EscapeMaskingChars: got %q want %q", got, want) - } + assert.Equal(t, "a\\\\b\\\"c", EscapeSpecialChars("a\\b\"c"), "unexpected EscapeSpecialChars") + assert.Equal(t, "\\*\\?\\^", EscapeMaskingChars("*?^"), "unexpected EscapeMaskingChars") } From a77e276f25df8e05f13312c248fbfc24e28c7198 Mon Sep 17 00:00:00 2001 From: Adam Dickmeiss Date: Tue, 3 Feb 2026 11:33:31 +0100 Subject: [PATCH 2/3] Fix relay error for ExprBuilder --- cqlbuilder/builder.go | 9 +++------ cqlbuilder/builder_test.go | 23 +++++++++++------------ 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/cqlbuilder/builder.go b/cqlbuilder/builder.go index 3e5312e..e48c2d6 100644 --- a/cqlbuilder/builder.go +++ b/cqlbuilder/builder.go @@ -267,6 +267,9 @@ func (eb *ExprBuilder) SortByModifiers(index string, mods ...cql.Modifier) *Expr // Build finalizes and returns the query. func (eb *ExprBuilder) Build() (cql.Query, error) { + if eb.err != nil { + return cql.Query{}, eb.err + } if eb.build == nil { return cql.Query{}, fmt.Errorf("builder is missing query context") } @@ -515,9 +518,6 @@ func (sb *SearchBuilder) termWithEscaper(term string, esc func(string) string) * // Escapes backslashes and quotes in a string. func EscapeSpecialChars(s string) string { - if s == "" { - return s - } s = strings.ReplaceAll(s, "\\", "\\\\") s = strings.ReplaceAll(s, "\"", "\\\"") return s @@ -525,9 +525,6 @@ func EscapeSpecialChars(s string) string { // Escapes masking characters (*, ?, ^) in a string. func EscapeMaskingChars(s string) string { - if s == "" { - return s - } s = strings.ReplaceAll(s, "*", "\\*") s = strings.ReplaceAll(s, "?", "\\?") s = strings.ReplaceAll(s, "^", "\\^") diff --git a/cqlbuilder/builder_test.go b/cqlbuilder/builder_test.go index b467cec..e12d200 100644 --- a/cqlbuilder/builder_test.go +++ b/cqlbuilder/builder_test.go @@ -83,11 +83,11 @@ func TestBuilderTermUnsafeEmpty(t *testing.T) { func TestBuilderValidation(t *testing.T) { _, err := NewQuery().Search("a").Term("").Build() assert.Error(t, err, "expected error for empty term") - assert.Equal(t, "query requires a root clause", err.Error()) + assert.Equal(t, "search term must be non-empty", err.Error()) _, err = NewQuery().Search("a").Rel("bogus").Term("x").Build() assert.Error(t, err, "expected error for invalid relation") - assert.Equal(t, "query requires a root clause", err.Error()) + assert.Equal(t, "invalid relation: \"bogus\"", err.Error()) _, err = NewQuery().SortBy("").Build() assert.Error(t, err, "expected error for empty sort index") @@ -219,7 +219,7 @@ func TestBuilderAppendErrors(t *testing.T) { Term("b"). Build() assert.Error(t, err, "cannot append boolean operator without existing root clause") - assert.Equal(t, "builder is missing query context", err.Error()) + assert.Equal(t, "query requires a root clause before appending", err.Error()) } func TestBuilderBeginClauseErrors(t *testing.T) { @@ -251,7 +251,7 @@ func TestBuilderJoinModifiersValidation(t *testing.T) { Term("d"). Build() assert.Error(t, err, "expected error for empty boolean modifier name") - assert.Equal(t, "builder is missing query context", err.Error()) + assert.Equal(t, "modifier name must be non-empty", err.Error()) _, err = NewQuery(). Search("a"). @@ -263,7 +263,7 @@ func TestBuilderJoinModifiersValidation(t *testing.T) { Build() assert.Error(t, err, "expected error for invalid boolean modifier relation") - assert.Equal(t, "builder is missing query context", err.Error()) + assert.Equal(t, "invalid modifier relation: \"bogus\"", err.Error()) } func TestBuilderRelationValidation(t *testing.T) { @@ -273,14 +273,14 @@ func TestBuilderRelationValidation(t *testing.T) { Term("b"). Build() assert.Error(t, err, "expected error for invalid relation") - assert.Equal(t, "query requires a root clause", err.Error()) + assert.Equal(t, "invalid relation: \"bogus\"", err.Error()) } func TestBuilderEndClauseWithoutStart(t *testing.T) { expr := &ExprBuilder{} _, err := expr.EndClause().Build() assert.Error(t, err, "expected error for EndClause without BeginClause") - assert.Equal(t, "builder is missing query context", err.Error()) + assert.Equal(t, "no open clause to end", err.Error()) } func TestBuilderMultiplePrefixes(t *testing.T) { @@ -353,7 +353,7 @@ func TestBuilderSearchModifiersValidation(t *testing.T) { Term("hello"). Build() assert.Error(t, err, "expected error for empty search modifier name") - assert.Equal(t, "query requires a root clause", err.Error()) + assert.Equal(t, "modifier name must be non-empty", err.Error()) _, err = NewQuery(). Search("title"). @@ -361,7 +361,7 @@ func TestBuilderSearchModifiersValidation(t *testing.T) { Term("hello"). Build() assert.Error(t, err, "expected error for invalid search modifier relation") - assert.Equal(t, "query requires a root clause", err.Error()) + assert.Equal(t, "invalid modifier relation: \"bogus\"", err.Error()) } func TestBuilderSearchRelationDefaults(t *testing.T) { @@ -384,9 +384,8 @@ func TestBuilderAppendInvalidOperator(t *testing.T) { left: expr.clause, op: "bogus", } - query, err := jb.Search("c").Term("d").Build() - assert.NoError(t, err, "unexpected error for invalid boolean operator") - assert.Equal(t, "a = b", query.String(), "unexpected query string") + _, err := jb.Search("c").Term("d").Build() + assert.Error(t, err, "expected error for invalid boolean operator") } func TestBuilderEscapeHelpers(t *testing.T) { From a29acefcd07e4b4d0449fe41dadacae12333c5a0 Mon Sep 17 00:00:00 2001 From: Adam Dickmeiss Date: Tue, 3 Feb 2026 11:50:59 +0100 Subject: [PATCH 3/3] Fix error msg; further tests --- cqlbuilder/builder.go | 2 +- cqlbuilder/builder_test.go | 35 +++++++++++++++++++++++++++++++++-- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/cqlbuilder/builder.go b/cqlbuilder/builder.go index e48c2d6..3f2702d 100644 --- a/cqlbuilder/builder.go +++ b/cqlbuilder/builder.go @@ -463,7 +463,7 @@ func (sb *SearchBuilder) ModRel(name cql.CqlModifier, rel cql.Relation, value st return sb } if strings.TrimSpace(string(name)) == "" { - sb.err = fmt.Errorf("modifier name must be non-empty") + sb.err = fmt.Errorf("modifier relation name must be non-empty") return sb } if rel == "" { diff --git a/cqlbuilder/builder_test.go b/cqlbuilder/builder_test.go index e12d200..0e58ec4 100644 --- a/cqlbuilder/builder_test.go +++ b/cqlbuilder/builder_test.go @@ -93,9 +93,13 @@ func TestBuilderValidation(t *testing.T) { assert.Error(t, err, "expected error for empty sort index") assert.Equal(t, "sort index must be non-empty", err.Error()) - _, err = NewQuery().Prefix("", "http://example.org/").Build() + _, err = NewQuery().Prefix("x", "").Build() assert.Error(t, err, "expected error for empty prefix") - assert.Equal(t, "query requires a root clause", err.Error()) + assert.Equal(t, "prefix uri must be non-empty", err.Error()) + + _, err = NewQuery().Prefix("x", "").Prefix("a", "b").SortBy("title").SortByModifiers("asc").Build() + assert.Error(t, err, "expected error for empty prefix") + assert.Equal(t, "prefix uri must be non-empty", err.Error()) } func TestBuilderDuplicateRoot(t *testing.T) { @@ -135,6 +139,9 @@ func TestBuilderFromString(t *testing.T) { query, err := qb.And().Search("author").Term("alice").Build() assert.NoError(t, err, "build failed") assert.Equal(t, "title = base and author = alice", query.String(), "unexpected query string") + + _, err = NewQueryFromString("a and") + assert.Error(t, err, "expected parse failed") } func TestBuilderGroupedClause(t *testing.T) { @@ -196,6 +203,7 @@ func TestBuilderErrorsAndModifiers(t *testing.T) { SortBy("title", ""). Build() assert.Error(t, err, "expected error for empty sort modifier name") + assert.Equal(t, "sort modifier name must be non-empty", err.Error()) _, err = NewQuery(). Search("a"). @@ -203,6 +211,7 @@ func TestBuilderErrorsAndModifiers(t *testing.T) { SortByModifiers("title", cql.Modifier{Name: "", Value: "x"}). Build() assert.Error(t, err, "expected error for empty modifier name") + assert.Equal(t, "sort modifier name must be non-empty", err.Error()) _, err = NewQuery(). Search("a"). @@ -210,6 +219,16 @@ func TestBuilderErrorsAndModifiers(t *testing.T) { SortByModifiers("title", cql.Modifier{Name: "x", Relation: "bogus"}). Build() assert.Error(t, err, "expected error for invalid modifier relation") + assert.Equal(t, "invalid modifier relation: \"bogus\"", err.Error()) + + _, err = NewQuery(). + Search("a"). + Term("b"). + SortByModifiers("", cql.Modifier{Name: "x", Relation: "="}). + Build() + assert.Error(t, err, "expected error for empty sort index") + assert.Equal(t, "sort index must be non-empty", err.Error()) + } func TestBuilderAppendErrors(t *testing.T) { @@ -253,6 +272,18 @@ func TestBuilderJoinModifiersValidation(t *testing.T) { assert.Error(t, err, "expected error for empty boolean modifier name") assert.Equal(t, "modifier name must be non-empty", err.Error()) + _, err = NewQuery(). + Search("a"). + Term("b"). + And(). + ModRel("", "bogus", "1"). + Search("c"). + Term("d"). + Build() + + assert.Error(t, err, "expected error for invalid boolean modifier relation") + assert.Equal(t, "modifier name must be non-empty", err.Error()) + _, err = NewQuery(). Search("a"). Term("b").