nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: zeromicro/go-zero
Pull request #1105 (2021-09-30 07:42:19)

Fix the `resources` variable not reset after the resource manager is closed


Patch:
diff --git a/core/syncx/resourcemanager.go b/core/syncx/resourcemanager.go
index 39e14561..cee3aba8 100644
--- a/core/syncx/resourcemanager.go
+++ b/core/syncx/resourcemanager.go
@@ -33,7 +33,8 @@ func (manager *ResourceManager) Close() error {
 			be.Add(err)
 		}
 	}
-
+	// reset resources
+	manager.resources = make(map[string]io.Closer)
 	return be.Err()
 }
 


Test patch:
diff --git a/core/syncx/resourcemanager_test.go b/core/syncx/resourcemanager_test.go
index 2debc84c..d4456bfa 100644
--- a/core/syncx/resourcemanager_test.go
+++ b/core/syncx/resourcemanager_test.go
@@ -44,3 +44,17 @@ func TestResourceManager_GetResourceError(t *testing.T) {
 		assert.NotNil(t, err)
 	}
 }
+
+func TestResourceManager_Close(t *testing.T) {
+	manager := NewResourceManager()
+
+	for i := 0; i < 10; i++ {
+		_, err := manager.GetResource("key", func() (io.Closer, error) {
+			return nil, errors.New("fail")
+		})
+		assert.NotNil(t, err)
+	}
+	err := manager.Close()
+	assert.NoError(t, err)
+	assert.Equal(t, 0, len(manager.resources))
+}
Repository: zeromicro/go-zero
Pull request #1106 (2021-10-01 01:15:35)

test: add more tests
1. add more tests for resourcemanager and spinlock
2. set resources to nil on `Close()`


Patch:
diff --git a/core/syncx/resourcemanager.go b/core/syncx/resourcemanager.go
index cee3aba8..a976cefd 100644
--- a/core/syncx/resourcemanager.go
+++ b/core/syncx/resourcemanager.go
@@ -23,6 +23,7 @@ func NewResourceManager() *ResourceManager {
 }
 
 // Close closes the manager.
+// Don't use the ResourceManager after Close() called.
 func (manager *ResourceManager) Close() error {
 	manager.lock.Lock()
 	defer manager.lock.Unlock()
@@ -33,8 +34,10 @@ func (manager *ResourceManager) Close() error {
 			be.Add(err)
 		}
 	}
-	// reset resources
-	manager.resources = make(map[string]io.Closer)
+
+	// release resources to avoid using it later
+	manager.resources = nil
+
 	return be.Err()
 }
 


Test patch:
diff --git a/core/syncx/resourcemanager_test.go b/core/syncx/resourcemanager_test.go
index d4456bfa..261bdb8e 100644
--- a/core/syncx/resourcemanager_test.go
+++ b/core/syncx/resourcemanager_test.go
@@ -47,14 +47,28 @@ func TestResourceManager_GetResourceError(t *testing.T) {
 
 func TestResourceManager_Close(t *testing.T) {
 	manager := NewResourceManager()
-
 	for i := 0; i < 10; i++ {
 		_, err := manager.GetResource("key", func() (io.Closer, error) {
 			return nil, errors.New("fail")
 		})
 		assert.NotNil(t, err)
 	}
-	err := manager.Close()
-	assert.NoError(t, err)
-	assert.Equal(t, 0, len(manager.resources))
+
+	if assert.NoError(t, manager.Close()) {
+		assert.Equal(t, 0, len(manager.resources))
+	}
+}
+
+func TestResourceManager_UseAfterClose(t *testing.T) {
+	manager := NewResourceManager()
+	_, err := manager.GetResource("key", func() (io.Closer, error) {
+		return nil, errors.New("fail")
+	})
+	assert.NotNil(t, err)
+	if assert.NoError(t, manager.Close()) {
+		_, err = manager.GetResource("key", func() (io.Closer, error) {
+			return nil, errors.New("fail")
+		})
+		assert.NotNil(t, err)
+	}
 }
diff --git a/core/syncx/spinlock_test.go b/core/syncx/spinlock_test.go
index 4fcc2bf9..e518d02d 100644
--- a/core/syncx/spinlock_test.go
+++ b/core/syncx/spinlock_test.go
@@ -1,11 +1,14 @@
 package syncx
 
 import (
+	"runtime"
 	"sync"
+	"sync/atomic"
 	"testing"
 	"time"
 
 	"github.com/stretchr/testify/assert"
+	"github.com/tal-tech/go-zero/core/lang"
 )
 
 func TestTryLock(t *testing.T) {
@@ -37,3 +40,31 @@ func TestSpinLockRace(t *testing.T) {
 	wait.Wait()
 	assert.True(t, lock.TryLock())
 }
+
+func TestSpinLock_TryLock(t *testing.T) {
+	var lock SpinLock
+	var count int32
+	var wait sync.WaitGroup
+	wait.Add(2)
+	sig := make(chan lang.PlaceholderType)
+
+	go func() {
+		lock.TryLock()
+		sig <- lang.Placeholder
+		atomic.AddInt32(&count, 1)
+		runtime.Gosched()
+		lock.Unlock()
+		wait.Done()
+	}()
+
+	go func() {
+		<-sig
+		lock.Lock()
+		atomic.AddInt32(&count, 1)
+		lock.Unlock()
+		wait.Done()
+	}()
+
+	wait.Wait()
+	assert.Equal(t, int32(2), atomic.LoadInt32(&count))
+}
Repository: zeromicro/go-zero
Pull request #1178 (2021-10-31 03:03:43)

docs: update roadmap


Patch:
diff --git a/ROADMAP.md b/ROADMAP.md
index 2b7b3702..72ff411a 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -10,13 +10,18 @@ We hope that the items listed below will inspire further engagement from the com
 
 ## 2021 Q3
 - [x] Support `goctl model pg` to support PostgreSQL code generation
-- [ ] Support `goctl mock` command to start a mocking server with given `.api` file
-- [ ] Adapt builtin tracing mechanism to opentracing solutions
+- [x] Adapt builtin tracing mechanism to opentracing solutions
 
 ## 2021 Q4
+- [x] Support `username/password` authentication in ETCD
+- [x] Support `SSL/TLS` in `zRPC`
+- [ ] Support `retry strategies` in `zRPC`
+- [ ] Support `TLS` in redis connections
+
+## 2022
+- [ ] Support `goctl mock` command to start a mocking server with given `.api` file
 - [ ] Add `httpx.Client` with governance, like circuit breaker etc.
 - [ ] Support `goctl doctor` command to report potential issues for given service
 - [ ] Support `context` in redis related methods for timeout and tracing
 - [ ] Support `context` in sql related methods for timeout and tracing
 - [ ] Support `context` in mongodb related methods for timeout and tracing
-- [ ] Support TLS in redis connections


Test patch:
diff --git a/zrpc/internal/client_test.go b/zrpc/internal/client_test.go
index af6eed78..a548d97c 100644
--- a/zrpc/internal/client_test.go
+++ b/zrpc/internal/client_test.go
@@ -24,6 +24,13 @@ func TestWithTimeout(t *testing.T) {
 	assert.Equal(t, time.Second, options.Timeout)
 }
 
+func TestWithRetry(t *testing.T) {
+	var options ClientOptions
+	opt := WithRetry()
+	opt(&options)
+	assert.True(t, options.Retry)
+}
+
 func TestWithUnaryClientInterceptor(t *testing.T) {
 	var options ClientOptions
 	opt := WithUnaryClientInterceptor(func(ctx context.Context, method string, req, reply interface{},
Repository: zeromicro/go-zero
Pull request #1183 (2021-10-31 11:22:28)

feat: support multiple trace agents


Patch:
diff --git a/core/trace/agent.go b/core/trace/agent.go
index af4f046f..64d8d01d 100644
--- a/core/trace/agent.go
+++ b/core/trace/agent.go
@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"sync"
 
+	"github.com/tal-tech/go-zero/core/lang"
 	"github.com/tal-tech/go-zero/core/logx"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/exporters/jaeger"
@@ -19,17 +20,31 @@ const (
 	kindZipkin = "zipkin"
 )
 
-var once sync.Once
+var (
+	agents = make(map[string]lang.PlaceholderType)
+	lock   sync.Mutex
+)
 
 // StartAgent starts a opentelemetry agent.
 func StartAgent(c Config) {
-	once.Do(func() {
-		startAgent(c)
-	})
+	lock.Lock()
+	defer lock.Unlock()
+
+	_, ok := agents[c.Endpoint]
+	if ok {
+		return
+	}
+
+	// if error happens, let later calls run.
+	if err := startAgent(c); err != nil {
+		return
+	}
+
+	agents[c.Endpoint] = lang.Placeholder
 }
 
 func createExporter(c Config) (sdktrace.SpanExporter, error) {
-	// Just support jaeger now, more for later
+	// Just support jaeger and zipkin now, more for later
 	switch c.Batcher {
 	case kindJaeger:
 		return jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(c.Endpoint)))
@@ -40,7 +55,7 @@ func createExporter(c Config) (sdktrace.SpanExporter, error) {
 	}
 }
 
-func startAgent(c Config) {
+func startAgent(c Config) error {
 	opts := []sdktrace.TracerProviderOption{
 		// Set the sampling rate based on the parent span to 100%
 		sdktrace.WithSampler(sdktrace.ParentBased(sdktrace.TraceIDRatioBased(c.Sampler))),
@@ -52,7 +67,7 @@ func startAgent(c Config) {
 		exp, err := createExporter(c)
 		if err != nil {
 			logx.Error(err)
-			return
+			return err
 		}
 
 		// Always be sure to batch in production.
@@ -66,4 +81,6 @@ func startAgent(c Config) {
 	otel.SetErrorHandler(otel.ErrorHandlerFunc(func(err error) {
 		logx.Errorf("[otel] error: %v", err)
 	}))
+
+	return nil
 }


Test patch:
diff --git a/core/trace/agent_test.go b/core/trace/agent_test.go
new file mode 100644
index 00000000..a15ad02c
--- /dev/null
+++ b/core/trace/agent_test.go
@@ -0,0 +1,54 @@
+package trace
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/tal-tech/go-zero/core/logx"
+)
+
+func TestStartAgent(t *testing.T) {
+	logx.Disable()
+
+	const (
+		endpoint1 = "localhost:1234"
+		endpoint2 = "remotehost:1234"
+		endpoint3 = "localhost:1235"
+	)
+	c1 := Config{
+		Name: "foo",
+	}
+	c2 := Config{
+		Name:     "bar",
+		Endpoint: endpoint1,
+		Batcher:  kindJaeger,
+	}
+	c3 := Config{
+		Name:     "any",
+		Endpoint: endpoint2,
+		Batcher:  kindZipkin,
+	}
+	c4 := Config{
+		Name:     "bla",
+		Endpoint: endpoint3,
+		Batcher:  "otlp",
+	}
+
+	StartAgent(c1)
+	StartAgent(c1)
+	StartAgent(c2)
+	StartAgent(c3)
+	StartAgent(c4)
+
+	lock.Lock()
+	defer lock.Unlock()
+
+	// because remotehost cannot be resolved
+	assert.Equal(t, 2, len(agents))
+	_, ok := agents[""]
+	assert.True(t, ok)
+	_, ok = agents[endpoint1]
+	assert.True(t, ok)
+	_, ok = agents[endpoint2]
+	assert.False(t, ok)
+}
Repository: zeromicro/go-zero
Pull request #1194 (2021-11-01 11:46:38)

feat: add rest.WithPrefix to support route prefix

zhoushuguang: +lgtm

Patch:
diff --git a/rest/server.go b/rest/server.go
index 0046712c..57d6434e 100644
--- a/rest/server.go
+++ b/rest/server.go
@@ -4,6 +4,7 @@ import (
 	"crypto/tls"
 	"log"
 	"net/http"
+	"path"
 
 	"github.com/tal-tech/go-zero/core/logx"
 	"github.com/tal-tech/go-zero/rest/handler"
@@ -161,6 +162,22 @@ func WithNotAllowedHandler(handler http.Handler) RunOption {
 	return WithRouter(rt)
 }
 
+// WithPrefix adds group as a prefix to the route paths.
+func WithPrefix(group string) RouteOption {
+	return func(r *featuredRoutes) {
+		var routes []Route
+		for _, rt := range r.routes {
+			p := path.Join(group, rt.Path)
+			routes = append(routes, Route{
+				Method:  rt.Method,
+				Path:    p,
+				Handler: rt.Handler,
+			})
+		}
+		r.routes = routes
+	}
+}
+
 // WithPriority returns a RunOption with priority.
 func WithPriority() RouteOption {
 	return func(r *featuredRoutes) {


Test patch:
diff --git a/rest/server_test.go b/rest/server_test.go
index 811e9ee4..30c2383b 100644
--- a/rest/server_test.go
+++ b/rest/server_test.go
@@ -213,6 +213,25 @@ func TestMultiMiddlewares(t *testing.T) {
 	}, m)
 }
 
+func TestWithPrefix(t *testing.T) {
+	fr := featuredRoutes{
+		routes: []Route{
+			{
+				Path: "/hello",
+			},
+			{
+				Path: "/world",
+			},
+		},
+	}
+	WithPrefix("/api")(&fr)
+	var vals []string
+	for _, r := range fr.routes {
+		vals = append(vals, r.Path)
+	}
+	assert.EqualValues(t, []string{"/api/hello", "/api/world"}, vals)
+}
+
 func TestWithPriority(t *testing.T) {
 	var fr featuredRoutes
 	WithPriority()(&fr)
Repository: diegoholiveira/jsonlogic
Pull request #62 (2022-12-11 18:31:34)

Fixed subtraction from zero
Subtraction from 0 was leading to wrong result, namely, positive value instead of negative one

codecov[bot]: # [Codecov](https://codecov.io/gh/diegoholiveira/jsonlogic/pull/62?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Diego+Oliveira) Report
> Merging [#62](https://codecov.io/gh/diegoholiveira/jsonlogic/pull/62?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Diego+Oliveira) (75e1964) into [master](https://codecov.io/gh/diegoholiveira/jsonlogic/commit/5c939b3613fca986fe07b9cd64a5ee520a56e7cd?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Diego+Oliveira) (5c939b3) will **increase** coverage by `0.00%`.
> The diff coverage is `100.00%`.

```diff
@@           Coverage Diff           @@
##           master      #62   +/-   ##
=======================================
  Coverage   95.82%   95.82%           
=======================================
  Files           9        9           
  Lines         957      959    +2     
=======================================
+ Hits          917      919    +2     
  Misses         28       28           
  Partials       12       12           
```



Patch:
diff --git a/math.go b/math.go
index 3ec1691..8c9f5a3 100644
--- a/math.go
+++ b/math.go
@@ -28,8 +28,10 @@ func sum(values interface{}) interface{} {
 func minus(values interface{}) interface{} {
 	var sum float64
 
+	isFirst := true
 	for _, n := range values.([]interface{}) {
-		if sum == 0 {
+		if isFirst {
+			isFirst = false
 			sum = toNumber(n)
 
 			continue


Test patch:
diff --git a/jsonlogic_test.go b/jsonlogic_test.go
index e7eee3c..4375244 100644
--- a/jsonlogic_test.go
+++ b/jsonlogic_test.go
@@ -741,3 +741,21 @@ func TestIssue58_example(t *testing.T) {
 	expected := `{"foo":"is_bar","path":"foo_is_bar"}`
 	assert.JSONEq(t, expected, result.String())
 }
+
+func TestSubOperation(t *testing.T) {
+	var rule json.RawMessage = json.RawMessage(`{
+		"-": [
+			0,
+			10
+		]
+	}`)
+
+	var expected json.RawMessage = json.RawMessage("-10")
+
+	output, err := ApplyRaw(rule, nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	assert.JSONEq(t, string(expected), string(output))
+}
Repository: diegoholiveira/jsonlogic
Pull request #64 (2023-01-11 18:24:35)

Fix null comparisons

codecov[bot]: # [Codecov](https://codecov.io/gh/diegoholiveira/jsonlogic/pull/64?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Diego+Oliveira) Report
> Merging [#64](https://codecov.io/gh/diegoholiveira/jsonlogic/pull/64?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Diego+Oliveira) (2137de4) into [master](https://codecov.io/gh/diegoholiveira/jsonlogic/commit/361f5711d24c2237fe64f19bf9ed8534d8d3c575?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Diego+Oliveira) (361f571) will **increase** coverage by `0.00%`.
> The diff coverage is `100.00%`.

```diff
@@           Coverage Diff           @@
##           master      #64   +/-   ##
=======================================
  Coverage   95.83%   95.84%           
=======================================
  Files           9        9           
  Lines         961      962    +1     
=======================================
+ Hits          921      922    +1     
  Misses         28       28           
  Partials       12       12           
```



Patch:
diff --git a/comp.go b/comp.go
index 34381d6..ad2343f 100644
--- a/comp.go
+++ b/comp.go
@@ -70,7 +70,8 @@ func hardEquals(a, b interface{}) bool {
 func equals(a, b interface{}) bool {
 	// comparison to a nil value is falsy
 	if a == nil || b == nil {
-		return false
+		// if a and b is nil, return true, else return falsy
+		return a == b
 	}
 
 	if isNumber(a) {


Test patch:
diff --git a/comp_test.go b/comp_test.go
index 69f6e83..f1bc012 100644
--- a/comp_test.go
+++ b/comp_test.go
@@ -63,3 +63,8 @@ func TestLessCompareNumberAndNil(t *testing.T) {
 func TestEqualsWithBooleans(t *testing.T) {
 	assert.True(t, equals(true, true))
 }
+
+func TestEqualsWithNil(t *testing.T) {
+	assert.True(t, equals(nil, nil))
+	assert.False(t, equals(nil, ""))
+}
\ No newline at end of file
Repository: diegoholiveira/jsonlogic
Pull request #82 (2024-04-16 12:06:26)

Ensure getVars only returns primitive values when value == <nil>

diegoholiveira: close #80 

Patch:
diff --git a/jsonlogic.go b/jsonlogic.go
index 704cbf5..6968101 100644
--- a/jsonlogic.go
+++ b/jsonlogic.go
@@ -423,8 +423,8 @@ func some(values, data interface{}) interface{} {
 	for _, value := range subject.([]interface{}) {
 		v := apply(
 			solveVars(
-				solveVars(parsed[1], value),
-				data,
+				solveVars(parsed[1], data),
+				value,
 			),
 			value,
 		)
diff --git a/vars.go b/vars.go
index b375eb9..10bf073 100644
--- a/vars.go
+++ b/vars.go
@@ -45,7 +45,14 @@ func solveVars(values, data interface{}) interface{} {
 }
 
 func getVar(value, data interface{}) interface{} {
-	if value == nil || (isString(value) && toString(value) == "") {
+	if value == nil {
+		if !isPrimitive(data) {
+			return nil
+		}
+		return data
+	}
+
+	if isString(value) && toString(value) == "" {
 		return data
 	}
 


Test patch:
diff --git a/jsonlogic_test.go b/jsonlogic_test.go
index feefb9c..5bda383 100644
--- a/jsonlogic_test.go
+++ b/jsonlogic_test.go
@@ -907,3 +907,28 @@ func TestIssue79(t *testing.T) {
 	expected := `true`
 	assert.JSONEq(t, expected, result.String())
 }
+
+func TestIssue81(t *testing.T) {
+	rule := `{
+      "some": [
+        {"var": "A"},
+        {"!=": [
+          {"var": ".B"},
+          {"var": "B"}
+        ]}
+      ]}
+         `
+
+	data := `{"A":[{"B":1}], "B":2}`
+
+	var result bytes.Buffer
+
+	err := Apply(strings.NewReader(rule), strings.NewReader(data), &result)
+
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	expected := `true`
+	assert.JSONEq(t, expected, result.String())
+}
Repository: diegoholiveira/jsonlogic
Pull request #103 (2025-02-10 17:52:19)

Fix minus with empty list


Patch:
diff --git a/math.go b/math.go
index 7dc634f..89c5412 100644
--- a/math.go
+++ b/math.go
@@ -26,11 +26,15 @@ func sum(values any) any {
 }
 
 func minus(values any) any {
-	_values := toSliceOfNumbers(values)
+	_values := values.([]any)
 
-	sum := _values[0]
+	if len(_values) == 0 {
+		return 0
+	}
+
+	sum := toNumber(_values[0])
 	for i := 1; len(_values) > i; i++ {
-		sum -= _values[i]
+		sum -= toNumber(_values[i])
 	}
 
 	return sum


Test patch:
diff --git a/jsonlogic_test.go b/jsonlogic_test.go
index ec4e8ec..b1b2bf7 100644
--- a/jsonlogic_test.go
+++ b/jsonlogic_test.go
@@ -641,3 +641,17 @@ func TestEqualWithList(t *testing.T) {
 
 	assert.JSONEq(t, `false`, result.String())
 }
+
+func TestMinusWithEmptyList(t *testing.T) {
+	rule := strings.NewReader(`{"-": []}`)
+	data := strings.NewReader(`null`)
+
+	var result bytes.Buffer
+
+	err := jsonlogic.Apply(rule, data, &result)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	assert.JSONEq(t, `0`, result.String())
+}
Repository: diegoholiveira/jsonlogic
Pull request #104 (2025-02-10 17:55:03)

Fix div with empty list


Patch:
diff --git a/helpers.go b/helpers.go
index 5212bab..cff98d9 100644
--- a/helpers.go
+++ b/helpers.go
@@ -70,16 +70,6 @@ func isTrue(obj any) bool {
 	return false
 }
 
-func toSliceOfNumbers(values any) []float64 {
-	_values := values.([]any)
-
-	numbers := make([]float64, len(_values))
-	for i, n := range _values {
-		numbers[i] = toNumber(n)
-	}
-	return numbers
-}
-
 func toNumber(value any) float64 {
 	if isString(value) {
 		w, _ := strconv.ParseFloat(value.(string), 64)
diff --git a/math.go b/math.go
index 89c5412..281965f 100644
--- a/math.go
+++ b/math.go
@@ -51,11 +51,15 @@ func mult(values any) any {
 }
 
 func div(values any) any {
-	_values := toSliceOfNumbers(values)
+	_values := values.([]any)
+
+	if len(_values) == 0 {
+		return 0
+	}
 
-	sum := _values[0]
+	sum := toNumber(_values[0])
 	for i := 1; len(_values) > i; i++ {
-		sum = sum / _values[i]
+		sum = sum / toNumber(_values[i])
 	}
 
 	return sum


Test patch:
diff --git a/jsonlogic_test.go b/jsonlogic_test.go
index b1b2bf7..2d8fb17 100644
--- a/jsonlogic_test.go
+++ b/jsonlogic_test.go
@@ -655,3 +655,17 @@ func TestMinusWithEmptyList(t *testing.T) {
 
 	assert.JSONEq(t, `0`, result.String())
 }
+
+func TestDivWithEmptyList(t *testing.T) {
+	rule := strings.NewReader(`{"/": []}`)
+	data := strings.NewReader(`null`)
+
+	var result bytes.Buffer
+
+	err := jsonlogic.Apply(rule, data, &result)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	assert.JSONEq(t, `0`, result.String())
+}
Repository: diegoholiveira/jsonlogic
Pull request #121 (2025-04-08 19:24:47)

fix: allow 'map primitives' in ValidateJsonLogic
Fixes https://github.com/diegoholiveira/jsonlogic/issues/120.


Patch:
diff --git a/validator.go b/validator.go
index 49ffa1f..c0237ee 100644
--- a/validator.go
+++ b/validator.go
@@ -26,7 +26,14 @@ func ValidateJsonLogic(rules any) bool {
 	}
 
 	if typing.IsMap(rules) {
-		for operator, value := range rules.(map[string]any) {
+		rulesMap := rules.(map[string]any)
+
+		// A map with more than 1 key counts as a primitive so it's time to end recursion
+		if len(rulesMap) > 1 {
+			return true
+		}
+
+		for operator, value := range rulesMap {
 			if !isOperator(operator) {
 				return false
 			}


Test patch:
diff --git a/validator_test.go b/validator_test.go
index dce743c..b7cbf57 100644
--- a/validator_test.go
+++ b/validator_test.go
@@ -36,6 +36,10 @@ func TestJSONLogicValidator(t *testing.T) {
 			IsValid: true,
 			Rule:    strings.NewReader(`10`),
 		},
+		"primitive map is a valid rule": {
+			IsValid: true,
+			Rule:    strings.NewReader(`{"if": [{">=": [{ "var": "amount" }, 10] }, { "var": "amount" }, { "output": true, "result": "too low" } ]}`),
+		},
 		"set must be valid": {
 			IsValid: true,
 			Rule: strings.NewReader(`{
Repository: diegoholiveira/jsonlogic
Pull request #123 (2025-04-10 12:12:11)

fix(122): Negating an empty slice should return true
Fixes https://github.com/diegoholiveira/jsonlogic/issues/122.


Patch:
diff --git a/logic.go b/logic.go
index 8243394..617386a 100644
--- a/logic.go
+++ b/logic.go
@@ -99,7 +99,8 @@ func conditional(values, data any) any {
 
 func negative(values, data any) any {
 	values = parseValues(values, data)
-	if typing.IsSlice(values) {
+	// If the slice is not empty, there is an argument to negate
+	if typing.IsSlice(values) && len(values.([]any)) > 0 {
 		return !typing.IsTrue(values.([]any)[0])
 	}
 	return !typing.IsTrue(values)


Test patch:
diff --git a/internal/json_logic_pr_48_tests.json b/internal/json_logic_pr_48_tests.json
index 74ffa58..fed0720 100644
--- a/internal/json_logic_pr_48_tests.json
+++ b/internal/json_logic_pr_48_tests.json
@@ -195,7 +195,10 @@
     [{"if":[ -1, "apple", "banana"]}, null, "apple"],
 
     "Truthy and falsy definitions matter in Boolean operations",
+    [{"and" : [ { "!": [ ] }, { "!": [ [] ] }, { "!": { "missing": "foo" } } ]}, {"foo": "bar"}, true],
+    [{"!" : []}, {}, true],
     [{"!" : [ [] ]}, {}, true],
+    [{"!!" : []}, {}, false],
     [{"!!" : [ [] ]}, {}, false],
     [{"and" : [ [], true ]}, {}, [] ],
     [{"or" : [ [], true ]}, {}, true ],