Skip to content

Incorrect handling near MaxInt64 #39

Closed
@zmerlynn

Description

@zmerlynn

In googleforgames/agones#3636, we noticed that jsonpatch is lossy near the int64 boundary.

I tested jsonpatch with the following patch:

diff --git a/v2/jsonpatch_test.go b/v2/jsonpatch_test.go
index c311159..7301dc7 100644
--- a/v2/jsonpatch_test.go
+++ b/v2/jsonpatch_test.go
@@ -2,6 +2,7 @@ package jsonpatch_test

 import (
        "encoding/json"
+       "fmt"
        "testing"

        jp "github.com/evanphx/json-patch"
@@ -16,6 +17,7 @@ var simpleD = `{"a":100, "b":200, "c":"hello", "d":"foo"}`
 var simpleE = `{"a":100, "b":200}`
 var simplef = `{"a":100, "b":100, "d":"foo"}`
 var simpleG = `{"a":100, "b":null, "d":"foo"}`
+var simpleH = `{"a":100, "b":200, "c":"hello", "d": 9223372036854775500}`
 var empty = `{}`

 var arraySrc = `
@@ -859,6 +861,7 @@ func TestCreatePatch(t *testing.T) {
                {"Simple:OneAdd", simpleA, simpleD},
                {"Simple:OneRemove", simpleA, simpleE},
                {"Simple:VsEmpty", simpleA, empty},
+               {"Simple:AddBigInt", simpleA, simpleH},
                // array types
                {"Array:Same", arraySrc, arraySrc},
                {"Array:BoolReplace", arraySrc, arrayDst},
@@ -912,6 +915,7 @@ func check(t *testing.T, src, dst string) {

        data, err := json.Marshal(patch)
        assert.Nil(t, err)
+       fmt.Printf("data = %v\n", patch)

        p2, err := jp.DecodePatch(data)
        assert.Nil(t, err)

and in the output, you can see what's happening:

$ go test -v -run TestCreatePatch/Simple:AddBigInt.*
=== RUN   TestCreatePatch
=== RUN   TestCreatePatch/Simple:AddBigInt[src->dst]
data = [{add /d 9.223372036854776e+18}]
=== RUN   TestCreatePatch/Simple:AddBigInt[dst->src]
data = [{remove /d <nil>}]
--- PASS: TestCreatePatch (0.00s)
    --- PASS: TestCreatePatch/Simple:AddBigInt[src->dst] (0.00s)
    --- PASS: TestCreatePatch/Simple:AddBigInt[dst->src] (0.00s)
PASS
ok  	gomodules.xyz/jsonpatch/v2	0.010s

Note that this test deceptively passes as well, I think because JSONEq translates them to the same lossy value.

I realize that this is not a case well supported by JSON, but it's how Kubernetes clients end up sending the value. :/

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions