From d68f39de9a4acb036fcba7c9c6a8af2c17563b3a Mon Sep 17 00:00:00 2001 From: Mildred Ki'Lya Date: Thu, 29 Oct 2015 22:51:31 +0100 Subject: [PATCH] Test JSON decoding and fix implentation Add a test that decodes a JSON object, and detect links in it. Then encode it again and it must match the original file. The test failed (because the link was a map[string]interface{} instead of a Node). The fix post-process the result of the JSON decoder to convert map[string]interface{} to Node. --- coding/Makefile | 10 ++++++ coding/coding.go | 5 +-- coding/coding_test.go | 54 ++++++++++++++++++++++++++++++++ coding/json.go | 71 +++++++++++++++++++++++++++++++++++++++++++ coding/json.testfile | 3 ++ 5 files changed, 139 insertions(+), 4 deletions(-) create mode 100644 coding/Makefile create mode 100644 coding/json.go create mode 100644 coding/json.testfile diff --git a/coding/Makefile b/coding/Makefile new file mode 100644 index 0000000..734bf30 --- /dev/null +++ b/coding/Makefile @@ -0,0 +1,10 @@ + +pb/bin/multicodec: + $(MAKE) -C pb bin/multicodec + +json.testfile: pb/bin/multicodec Makefile + : >$@ + pb/bin/multicodec header /multicodec >>$@ + pb/bin/multicodec header /json >>$@ + echo '{"@codec":"/json","abc":{"mlink":"QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39V"}}' >>$@ + diff --git a/coding/coding.go b/coding/coding.go index 5c15583..ac4083e 100644 --- a/coding/coding.go +++ b/coding/coding.go @@ -3,7 +3,6 @@ package ipfsld import ( mc "github.com/jbenet/go-multicodec" mccbor "github.com/jbenet/go-multicodec/cbor" - mcjson "github.com/jbenet/go-multicodec/json" mcmux "github.com/jbenet/go-multicodec/mux" ipld "github.com/ipfs/go-ipld" @@ -24,7 +23,7 @@ func init() { defaultCodec = string(mc.HeaderPath(mccbor.Header)) muxCodec = mcmux.MuxMulticodec([]mc.Multicodec{ mccbor.Multicodec(), - mcjson.Multicodec(false), + jsonMulticodec(), pb.Multicodec(), }, selectCodec) } @@ -61,8 +60,6 @@ func codecKey(n ipld.Node) (string, error) { if !ok { // if no codec is defined, use our default codec chdr = defaultCodec - - // except, if it looks like an old, style protobuf object if pb.IsOldProtobufNode(n) { chdr = string(pb.Header) } diff --git a/coding/coding_test.go b/coding/coding_test.go index 427683d..a56712c 100644 --- a/coding/coding_test.go +++ b/coding/coding_test.go @@ -1,13 +1,27 @@ package ipfsld import ( + "io/ioutil" "testing" + "reflect" + "bytes" ipld "github.com/ipfs/go-ipld" + mc "github.com/jbenet/go-multicodec" mctest "github.com/jbenet/go-multicodec/test" ) +var json_testfile []byte + +func init() { + var err error + json_testfile, err = ioutil.ReadFile("json.testfile") + if err != nil { + panic("could not read json.testfile. please run: make json.testfile") + } +} + type TC struct { cbor []byte src ipld.Node @@ -86,3 +100,43 @@ func TestRoundtripBasicMC(t *testing.T) { mctest.RoundTripTest(t, codec, &(tca.src), &tcb) } } + +// Test decoding and encoding a json file +func TestJsonDecodeEncode(t *testing.T) { + var n ipld.Node + codec := Multicodec() + + if err := mc.Unmarshal(codec, json_testfile, &n); err != nil { + t.Log(json_testfile) + t.Error(err) + return + } + + linksExpected := map[string]ipld.Link{ + "abc": ipld.Link { + "mlink": "QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39V", + }, + } + linksActual := ipld.Links(n) + if !reflect.DeepEqual(linksExpected, linksActual) { + t.Log(linksExpected) + t.Log(linksActual) + t.Logf("node: %#v\n", n) + t.Fatalf("Links are not expected") + } + + encoded, err := mc.Marshal(codec, &n) + if err != nil { + t.Error(err) + return + } + + if !bytes.Equal(json_testfile, encoded) { + t.Error("marshalled values not equal") + t.Log(string(json_testfile)) + t.Log(string(encoded)) + t.Log(json_testfile) + t.Log(encoded) + } +} + diff --git a/coding/json.go b/coding/json.go new file mode 100644 index 0000000..b41c6e9 --- /dev/null +++ b/coding/json.go @@ -0,0 +1,71 @@ +package ipfsld + +import ( + "io" + + mc "github.com/jbenet/go-multicodec" + mcjson "github.com/jbenet/go-multicodec/json" + ipld "github.com/ipfs/go-ipld" +) + +type codec struct { + mc.Multicodec +} + +type decoder struct { + mc.Decoder +} + +func jsonMulticodec() mc.Multicodec { + return &codec{mcjson.Multicodec(false)} +} + +func (c *codec) Decoder(r io.Reader) mc.Decoder { + return &decoder{ c.Multicodec.Decoder(r) } +} + +func (c *decoder) Decode(v interface{}) error { + err := c.Decoder.Decode(v) + if err == nil { + convert(v) + } + return err +} + +func convert(val interface{}) interface{} { + switch val.(type) { + case *map[string]interface{}: + vmi := val.(*map[string]interface{}) + n := ipld.Node{} + for k, v := range *vmi { + n[k] = convert(v) + (*vmi)[k] = convert(v) + } + return &n + case map[string]interface{}: + vmi := val.(map[string]interface{}) + n := ipld.Node{} + for k, v := range vmi { + n[k] = convert(v) + vmi[k] = convert(v) + } + return n + case *[]interface{}: + convert(*val.(*[]interface{})) + case []interface{}: + slice := val.([]interface{}) + for k, v := range slice { + slice[k] = convert(v) + } + case *ipld.Node: + convert(*val.(*ipld.Node)) + case ipld.Node: + n := val.(ipld.Node) + for k, v := range n { + n[k] = convert(v) + } + default: + } + return val +} + diff --git a/coding/json.testfile b/coding/json.testfile new file mode 100644 index 0000000..5475f67 --- /dev/null +++ b/coding/json.testfile @@ -0,0 +1,3 @@ + /multicodec +/json +{"@codec":"/json","abc":{"mlink":"QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39V"}}