package orm import ( "fmt" "testing" ) func TestDomainCompileEmpty(t *testing.T) { dc := &DomainCompiler{model: &Model{table: "test"}} where, params, err := dc.Compile(nil) if err != nil { t.Fatal(err) } if where != "TRUE" { t.Errorf("expected TRUE, got %s", where) } if len(params) != 0 { t.Errorf("expected 0 params, got %d", len(params)) } } func TestDomainCompileSimpleLeaf(t *testing.T) { dc := &DomainCompiler{model: &Model{table: "test"}} domain := Domain{Leaf("name", "=", "test")} where, params, err := dc.Compile(domain) if err != nil { t.Fatal(err) } if where != `"name" = $1` { t.Errorf("got %s", where) } if len(params) != 1 || params[0] != "test" { t.Errorf("params: %v", params) } } func TestDomainCompileNullCheck(t *testing.T) { dc := &DomainCompiler{model: &Model{table: "test"}} domain := Domain{Leaf("name", "=", nil)} where, _, err := dc.Compile(domain) if err != nil { t.Fatal(err) } if where != `"name" IS NULL` { t.Errorf("got %s", where) } } func TestDomainCompileFalseCheck(t *testing.T) { dc := &DomainCompiler{model: &Model{table: "test"}} domain := Domain{Leaf("active", "=", false)} where, _, err := dc.Compile(domain) if err != nil { t.Fatal(err) } if where != `"active" IS NULL` { t.Errorf("got %s", where) } } func TestDomainCompileNotEqualNull(t *testing.T) { dc := &DomainCompiler{model: &Model{table: "test"}} domain := Domain{Leaf("name", "!=", nil)} where, _, err := dc.Compile(domain) if err != nil { t.Fatal(err) } if where != `"name" IS NOT NULL` { t.Errorf("got %s", where) } } func TestDomainCompileIn(t *testing.T) { dc := &DomainCompiler{model: &Model{table: "test"}} domain := Domain{Leaf("id", "in", []int64{1, 2, 3})} where, params, err := dc.Compile(domain) if err != nil { t.Fatal(err) } if where != `"id" IN ($1, $2, $3)` { t.Errorf("got %s", where) } if len(params) != 3 { t.Errorf("params: %v", params) } } func TestDomainCompileEmptyIn(t *testing.T) { dc := &DomainCompiler{model: &Model{table: "test"}} domain := Domain{Leaf("id", "in", []int64{})} where, _, err := dc.Compile(domain) if err != nil { t.Fatal(err) } if where != "FALSE" { t.Errorf("got %s", where) } } func TestDomainCompileNotIn(t *testing.T) { dc := &DomainCompiler{model: &Model{table: "test"}} domain := Domain{Leaf("id", "not in", []int64{1, 2})} where, params, err := dc.Compile(domain) if err != nil { t.Fatal(err) } if where != `"id" NOT IN ($1, $2)` { t.Errorf("got %s", where) } if len(params) != 2 { t.Errorf("params: %v", params) } } func TestDomainCompileEmptyNotIn(t *testing.T) { dc := &DomainCompiler{model: &Model{table: "test"}} domain := Domain{Leaf("id", "not in", []int64{})} where, _, err := dc.Compile(domain) if err != nil { t.Fatal(err) } if where != "TRUE" { t.Errorf("got %s", where) } } func TestDomainCompileLike(t *testing.T) { dc := &DomainCompiler{model: &Model{table: "test"}} domain := Domain{Leaf("name", "ilike", "test")} where, params, err := dc.Compile(domain) if err != nil { t.Fatal(err) } if where != `"name" ILIKE $1` { t.Errorf("got %s", where) } if params[0] != "%test%" { t.Errorf("expected %%test%%, got %v", params[0]) } } func TestDomainCompileLikeWithWildcard(t *testing.T) { dc := &DomainCompiler{model: &Model{table: "test"}} domain := Domain{Leaf("name", "ilike", "test%")} where, params, err := dc.Compile(domain) if err != nil { t.Fatal(err) } if where != `"name" ILIKE $1` { t.Errorf("got %s", where) } // Value already contains wildcard, should not be wrapped again if params[0] != "test%" { t.Errorf("expected test%%, got %v", params[0]) } } func TestDomainCompileNotLike(t *testing.T) { dc := &DomainCompiler{model: &Model{table: "test"}} domain := Domain{Leaf("name", "not like", "foo")} where, params, err := dc.Compile(domain) if err != nil { t.Fatal(err) } if where != `"name" NOT LIKE $1` { t.Errorf("got %s", where) } if params[0] != "%foo%" { t.Errorf("expected %%foo%%, got %v", params[0]) } } func TestDomainCompileExactLike(t *testing.T) { dc := &DomainCompiler{model: &Model{table: "test"}} domain := Domain{Leaf("name", "=like", "test")} where, params, err := dc.Compile(domain) if err != nil { t.Fatal(err) } if where != `"name" LIKE $1` { t.Errorf("got %s", where) } // =like does NOT auto-wrap if params[0] != "test" { t.Errorf("expected test, got %v", params[0]) } } func TestDomainCompileExactIlike(t *testing.T) { dc := &DomainCompiler{model: &Model{table: "test"}} domain := Domain{Leaf("name", "=ilike", "Test")} where, params, err := dc.Compile(domain) if err != nil { t.Fatal(err) } if where != `"name" ILIKE $1` { t.Errorf("got %s", where) } if params[0] != "Test" { t.Errorf("expected Test, got %v", params[0]) } } func TestDomainCompileAnd(t *testing.T) { dc := &DomainCompiler{model: &Model{table: "test"}} domain := And(Leaf("a", "=", 1), Leaf("b", "=", 2)) where, params, err := dc.Compile(domain) if err != nil { t.Fatal(err) } expected := `("a" = $1 AND "b" = $2)` if where != expected { t.Errorf("expected %s, got %s", expected, where) } if len(params) != 2 { t.Errorf("params: %v", params) } } func TestDomainCompileOr(t *testing.T) { dc := &DomainCompiler{model: &Model{table: "test"}} domain := Or(Leaf("a", "=", 1), Leaf("b", "=", 2)) where, _, err := dc.Compile(domain) if err != nil { t.Fatal(err) } expected := `("a" = $1 OR "b" = $2)` if where != expected { t.Errorf("expected %s, got %s", expected, where) } } func TestDomainCompileNot(t *testing.T) { dc := &DomainCompiler{model: &Model{table: "test"}} domain := Not(Leaf("active", "=", true)) where, _, err := dc.Compile(domain) if err != nil { t.Fatal(err) } expected := `(NOT "active" = $1)` if where != expected { t.Errorf("expected %s, got %s", expected, where) } } func TestDomainCompileInvalidOperator(t *testing.T) { dc := &DomainCompiler{model: &Model{table: "test"}} domain := Domain{Leaf("a", "INVALID", 1)} _, _, err := dc.Compile(domain) if err == nil { t.Error("expected error for invalid operator") } } func TestDomainCompileComparison(t *testing.T) { ops := []string{"<", ">", "<=", ">="} for _, op := range ops { t.Run(op, func(t *testing.T) { dc := &DomainCompiler{model: &Model{table: "test"}} domain := Domain{Leaf("age", op, 18)} where, params, err := dc.Compile(domain) if err != nil { t.Fatal(err) } expected := fmt.Sprintf(`"age" %s $1`, op) if where != expected { t.Errorf("expected %s, got %s", expected, where) } if len(params) != 1 || params[0] != 18 { t.Errorf("params: %v", params) } }) } } func TestDomainCompileInStrings(t *testing.T) { dc := &DomainCompiler{model: &Model{table: "test"}} domain := Domain{Leaf("state", "in", []string{"draft", "sent"})} where, params, err := dc.Compile(domain) if err != nil { t.Fatal(err) } if where != `"state" IN ($1, $2)` { t.Errorf("got %s", where) } if len(params) != 2 { t.Errorf("params: %v", params) } } func TestAndEmpty(t *testing.T) { d := And() if d != nil { t.Errorf("expected nil, got %v", d) } } func TestAndSingle(t *testing.T) { d := And(Leaf("a", "=", 1)) if len(d) != 1 { t.Errorf("expected 1 node, got %d", len(d)) } } func TestOrEmpty(t *testing.T) { d := Or() if d != nil { t.Errorf("expected nil, got %v", d) } } func TestOrSingle(t *testing.T) { d := Or(Leaf("a", "=", 1)) if len(d) != 1 { t.Errorf("expected 1 node, got %d", len(d)) } } func TestOrMultiple(t *testing.T) { d := Or(Leaf("a", "=", 1), Leaf("b", "=", 2), Leaf("c", "=", 3)) // Should have 2 OR operators + 3 leaves = 5 nodes if len(d) != 5 { t.Errorf("expected 5 nodes, got %d", len(d)) } } func TestAndMultiple(t *testing.T) { d := And(Leaf("a", "=", 1), Leaf("b", "=", 2), Leaf("c", "=", 3)) // Should have 2 AND operators + 3 leaves = 5 nodes if len(d) != 5 { t.Errorf("expected 5 nodes, got %d", len(d)) } } func TestNotDomain(t *testing.T) { d := Not(Leaf("active", "=", true)) if len(d) != 2 { t.Errorf("expected 2 nodes, got %d", len(d)) } if d[0] != OpNot { t.Errorf("expected OpNot, got %v", d[0]) } } func TestLeafCreation(t *testing.T) { c := Leaf("name", "=", "test") if c.Field != "name" { t.Errorf("field: %s", c.Field) } if c.Operator != "=" { t.Errorf("operator: %s", c.Operator) } if c.Value != "test" { t.Errorf("value: %v", c.Value) } } func TestWrapLikeValue(t *testing.T) { tests := []struct { name string input Value want Value }{ {"plain string", "test", "%test%"}, {"already has %", "test%", "test%"}, {"already has _", "test_val", "test_val"}, {"non-string", 42, 42}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := wrapLikeValue(tt.input) if got != tt.want { t.Errorf("wrapLikeValue(%v) = %v, want %v", tt.input, got, tt.want) } }) } } func TestNormalizeSlice(t *testing.T) { t.Run("[]int64", func(t *testing.T) { result := normalizeSlice([]int64{1, 2, 3}) if len(result) != 3 { t.Errorf("expected 3, got %d", len(result)) } }) t.Run("[]string", func(t *testing.T) { result := normalizeSlice([]string{"a", "b"}) if len(result) != 2 { t.Errorf("expected 2, got %d", len(result)) } }) t.Run("[]int", func(t *testing.T) { result := normalizeSlice([]int{1, 2}) if len(result) != 2 { t.Errorf("expected 2, got %d", len(result)) } }) t.Run("[]float64", func(t *testing.T) { result := normalizeSlice([]float64{1.5, 2.5}) if len(result) != 2 { t.Errorf("expected 2, got %d", len(result)) } }) t.Run("non-slice", func(t *testing.T) { result := normalizeSlice("not a slice") if result != nil { t.Errorf("expected nil, got %v", result) } }) } func TestToInt64Slice(t *testing.T) { tests := []struct { name string val Value want []int64 }{ {"int64", int64(5), []int64{5}}, {"int", int(3), []int64{3}}, {"int32", int32(7), []int64{7}}, {"float64", float64(9), []int64{9}}, {"[]int64", []int64{1, 2}, []int64{1, 2}}, {"[]int", []int{3, 4}, []int64{3, 4}}, {"string", "bad", nil}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := toInt64Slice(tt.val) if tt.want == nil { if got != nil { t.Errorf("expected nil, got %v", got) } return } if len(got) != len(tt.want) { t.Fatalf("len: got %d, want %d", len(got), len(tt.want)) } for i := range got { if got[i] != tt.want[i] { t.Errorf("index %d: got %d, want %d", i, got[i], tt.want[i]) } } }) } }