github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/rpc/jsoncodec/codec_test.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package jsoncodec_test
     5  
     6  import (
     7  	"encoding/json"
     8  	"errors"
     9  	"io"
    10  	"reflect"
    11  	stdtesting "testing"
    12  
    13  	"github.com/juju/testing"
    14  	jc "github.com/juju/testing/checkers"
    15  	gc "gopkg.in/check.v1"
    16  
    17  	"github.com/juju/juju/rpc"
    18  	"github.com/juju/juju/rpc/jsoncodec"
    19  )
    20  
    21  type suite struct {
    22  	testing.LoggingSuite
    23  }
    24  
    25  var _ = gc.Suite(&suite{})
    26  
    27  func TestPackage(t *stdtesting.T) {
    28  	gc.TestingT(t)
    29  }
    30  
    31  type value struct {
    32  	X string
    33  }
    34  
    35  func (*suite) TestRead(c *gc.C) {
    36  	for i, test := range []struct {
    37  		msg        string
    38  		expectHdr  rpc.Header
    39  		expectBody interface{}
    40  	}{{
    41  		msg: `{"RequestId": 1, "Type": "foo", "Id": "id", "Request": "frob", "Params": {"X": "param"}}`,
    42  		expectHdr: rpc.Header{
    43  			RequestId: 1,
    44  			Request: rpc.Request{
    45  				Type:   "foo",
    46  				Id:     "id",
    47  				Action: "frob",
    48  			},
    49  		},
    50  		expectBody: &value{X: "param"},
    51  	}, {
    52  		msg: `{"RequestId": 2, "Error": "an error", "ErrorCode": "a code"}`,
    53  		expectHdr: rpc.Header{
    54  			RequestId: 2,
    55  			Error:     "an error",
    56  			ErrorCode: "a code",
    57  		},
    58  		expectBody: new(map[string]interface{}),
    59  	}, {
    60  		msg: `{"RequestId": 3, "Response": {"X": "result"}}`,
    61  		expectHdr: rpc.Header{
    62  			RequestId: 3,
    63  		},
    64  		expectBody: &value{X: "result"},
    65  	}, {
    66  		msg: `{"RequestId": 4, "Type": "foo", "Version": 2, "Id": "id", "Request": "frob", "Params": {"X": "param"}}`,
    67  		expectHdr: rpc.Header{
    68  			RequestId: 4,
    69  			Request: rpc.Request{
    70  				Type:    "foo",
    71  				Version: 2,
    72  				Id:      "id",
    73  				Action:  "frob",
    74  			},
    75  		},
    76  		expectBody: &value{X: "param"},
    77  	}, {
    78  		msg: `{"request-id": 1, "type": "foo", "id": "id", "request": "frob", "params": {"X": "param"}}`,
    79  		expectHdr: rpc.Header{
    80  			RequestId: 1,
    81  			Request: rpc.Request{
    82  				Type:   "foo",
    83  				Id:     "id",
    84  				Action: "frob",
    85  			},
    86  			Version: 1,
    87  		},
    88  		expectBody: &value{X: "param"},
    89  	}, {
    90  		msg: `{"request-id": 2, "error": "an error", "error-code": "a code"}`,
    91  		expectHdr: rpc.Header{
    92  			RequestId: 2,
    93  			Error:     "an error",
    94  			ErrorCode: "a code",
    95  			Version:   1,
    96  		},
    97  		expectBody: new(map[string]interface{}),
    98  	}, {
    99  		msg: `{"request-id": 3, "response": {"X": "result"}}`,
   100  		expectHdr: rpc.Header{
   101  			RequestId: 3,
   102  			Version:   1,
   103  		},
   104  		expectBody: &value{X: "result"},
   105  	}, {
   106  		msg: `{"request-id": 4, "type": "foo", "version": 2, "id": "id", "request": "frob", "params": {"X": "param"}}`,
   107  		expectHdr: rpc.Header{
   108  			RequestId: 4,
   109  			Request: rpc.Request{
   110  				Type:    "foo",
   111  				Version: 2,
   112  				Id:      "id",
   113  				Action:  "frob",
   114  			},
   115  			Version: 1,
   116  		},
   117  		expectBody: &value{X: "param"},
   118  	}} {
   119  		c.Logf("test %d", i)
   120  		codec := jsoncodec.New(&testConn{
   121  			readMsgs: []string{test.msg},
   122  		})
   123  		var hdr rpc.Header
   124  		err := codec.ReadHeader(&hdr)
   125  		c.Assert(err, jc.ErrorIsNil)
   126  		c.Assert(hdr, gc.DeepEquals, test.expectHdr)
   127  
   128  		c.Assert(hdr.IsRequest(), gc.Equals, test.expectHdr.IsRequest())
   129  
   130  		body := reflect.New(reflect.ValueOf(test.expectBody).Type().Elem()).Interface()
   131  		err = codec.ReadBody(body, test.expectHdr.IsRequest())
   132  		c.Assert(err, jc.ErrorIsNil)
   133  		c.Assert(body, gc.DeepEquals, test.expectBody)
   134  
   135  		err = codec.ReadHeader(&hdr)
   136  		c.Assert(err, gc.Equals, io.EOF)
   137  	}
   138  }
   139  
   140  func (*suite) TestErrorAfterClose(c *gc.C) {
   141  	conn := &testConn{
   142  		err: errors.New("some error"),
   143  	}
   144  	codec := jsoncodec.New(conn)
   145  	var hdr rpc.Header
   146  	err := codec.ReadHeader(&hdr)
   147  	c.Assert(err, gc.ErrorMatches, "error receiving message: some error")
   148  
   149  	err = codec.Close()
   150  	c.Assert(err, jc.ErrorIsNil)
   151  	c.Assert(conn.closed, jc.IsTrue)
   152  
   153  	err = codec.ReadHeader(&hdr)
   154  	c.Assert(err, gc.Equals, io.EOF)
   155  }
   156  
   157  func (*suite) TestWrite(c *gc.C) {
   158  	for i, test := range []struct {
   159  		hdr       *rpc.Header
   160  		body      interface{}
   161  		isRequest bool
   162  		expect    string
   163  	}{{
   164  		hdr: &rpc.Header{
   165  			RequestId: 1,
   166  			Request: rpc.Request{
   167  				Type:   "foo",
   168  				Id:     "id",
   169  				Action: "frob",
   170  			},
   171  		},
   172  		body:   &value{X: "param"},
   173  		expect: `{"RequestId": 1, "Type": "foo","Id":"id", "Request": "frob", "Params": {"X": "param"}}`,
   174  	}, {
   175  		hdr: &rpc.Header{
   176  			RequestId: 2,
   177  			Error:     "an error",
   178  			ErrorCode: "a code",
   179  		},
   180  		expect: `{"RequestId": 2, "Error": "an error", "ErrorCode": "a code"}`,
   181  	}, {
   182  		hdr: &rpc.Header{
   183  			RequestId: 3,
   184  		},
   185  		body:   &value{X: "result"},
   186  		expect: `{"RequestId": 3, "Response": {"X": "result"}}`,
   187  	}, {
   188  		hdr: &rpc.Header{
   189  			RequestId: 4,
   190  			Request: rpc.Request{
   191  				Type:    "foo",
   192  				Version: 2,
   193  				Id:      "",
   194  				Action:  "frob",
   195  			},
   196  		},
   197  		body:   &value{X: "param"},
   198  		expect: `{"RequestId": 4, "Type": "foo", "Version": 2, "Request": "frob", "Params": {"X": "param"}}`,
   199  	}, {
   200  		hdr: &rpc.Header{
   201  			RequestId: 1,
   202  			Request: rpc.Request{
   203  				Type:   "foo",
   204  				Id:     "id",
   205  				Action: "frob",
   206  			},
   207  			Version: 1,
   208  		},
   209  		body:   &value{X: "param"},
   210  		expect: `{"request-id": 1, "type": "foo","id":"id", "request": "frob", "params": {"X": "param"}}`,
   211  	}, {
   212  		hdr: &rpc.Header{
   213  			RequestId: 2,
   214  			Error:     "an error",
   215  			ErrorCode: "a code",
   216  			Version:   1,
   217  		},
   218  		expect: `{"request-id": 2, "error": "an error", "error-code": "a code"}`,
   219  	}, {
   220  		hdr: &rpc.Header{
   221  			RequestId: 3,
   222  			Version:   1,
   223  		},
   224  		body:   &value{X: "result"},
   225  		expect: `{"request-id": 3, "response": {"X": "result"}}`,
   226  	}, {
   227  		hdr: &rpc.Header{
   228  			RequestId: 4,
   229  			Request: rpc.Request{
   230  				Type:    "foo",
   231  				Version: 2,
   232  				Id:      "",
   233  				Action:  "frob",
   234  			},
   235  			Version: 1,
   236  		},
   237  		body:   &value{X: "param"},
   238  		expect: `{"request-id": 4, "type": "foo", "version": 2, "request": "frob", "params": {"X": "param"}}`,
   239  	}} {
   240  		c.Logf("test %d", i)
   241  		var conn testConn
   242  		codec := jsoncodec.New(&conn)
   243  		err := codec.WriteMessage(test.hdr, test.body)
   244  		c.Assert(err, jc.ErrorIsNil)
   245  		c.Assert(conn.writeMsgs, gc.HasLen, 1)
   246  
   247  		assertJSONEqual(c, conn.writeMsgs[0], test.expect)
   248  	}
   249  }
   250  
   251  func (*suite) TestDumpRequest(c *gc.C) {
   252  	for i, test := range []struct {
   253  		hdr    rpc.Header
   254  		body   interface{}
   255  		expect string
   256  	}{{
   257  		hdr: rpc.Header{
   258  			RequestId: 1,
   259  			Request: rpc.Request{
   260  				Type:   "Foo",
   261  				Id:     "id",
   262  				Action: "Something",
   263  			},
   264  		},
   265  		body:   struct{ Arg string }{Arg: "an arg"},
   266  		expect: `{"RequestId":1,"Type":"Foo","Id":"id","Request":"Something","Params":{"Arg":"an arg"}}`,
   267  	}, {
   268  		hdr: rpc.Header{
   269  			RequestId: 2,
   270  		},
   271  		body:   struct{ Ret string }{Ret: "return value"},
   272  		expect: `{"RequestId":2,"Response":{"Ret":"return value"}}`,
   273  	}, {
   274  		hdr: rpc.Header{
   275  			RequestId: 3,
   276  		},
   277  		expect: `{"RequestId":3}`,
   278  	}, {
   279  		hdr: rpc.Header{
   280  			RequestId: 4,
   281  			Error:     "an error",
   282  			ErrorCode: "an error code",
   283  		},
   284  		expect: `{"RequestId":4,"Error":"an error","ErrorCode":"an error code"}`,
   285  	}, {
   286  		hdr: rpc.Header{
   287  			RequestId: 5,
   288  		},
   289  		body:   make(chan int),
   290  		expect: `"marshal error: json: unsupported type: chan int"`,
   291  	}, {
   292  		hdr: rpc.Header{
   293  			RequestId: 1,
   294  			Request: rpc.Request{
   295  				Type:    "Foo",
   296  				Version: 2,
   297  				Id:      "id",
   298  				Action:  "Something",
   299  			},
   300  		},
   301  		body:   struct{ Arg string }{Arg: "an arg"},
   302  		expect: `{"RequestId":1,"Type":"Foo","Version":2,"Id":"id","Request":"Something","Params":{"Arg":"an arg"}}`,
   303  	}, {
   304  		hdr: rpc.Header{
   305  			RequestId: 1,
   306  			Request: rpc.Request{
   307  				Type:   "Foo",
   308  				Id:     "id",
   309  				Action: "Something",
   310  			},
   311  			Version: 1,
   312  		},
   313  		body:   struct{ Arg string }{Arg: "an arg"},
   314  		expect: `{"request-id":1,"type":"Foo","id":"id","request":"Something","params":{"Arg":"an arg"}}`,
   315  	}, {
   316  		hdr: rpc.Header{
   317  			RequestId: 2,
   318  			Version:   1,
   319  		},
   320  		body:   struct{ Ret string }{Ret: "return value"},
   321  		expect: `{"request-id":2,"response":{"Ret":"return value"}}`,
   322  	}, {
   323  		hdr: rpc.Header{
   324  			RequestId: 3,
   325  			Version:   1,
   326  		},
   327  		expect: `{"request-id":3}`,
   328  	}, {
   329  		hdr: rpc.Header{
   330  			RequestId: 4,
   331  			Error:     "an error",
   332  			ErrorCode: "an error code",
   333  			Version:   1,
   334  		},
   335  		expect: `{"request-id":4,"error":"an error","error-code":"an error code"}`,
   336  	}, {
   337  		hdr: rpc.Header{
   338  			RequestId: 5,
   339  			Version:   1,
   340  		},
   341  		body:   make(chan int),
   342  		expect: `"marshal error: json: unsupported type: chan int"`,
   343  	}, {
   344  		hdr: rpc.Header{
   345  			RequestId: 1,
   346  			Request: rpc.Request{
   347  				Type:    "Foo",
   348  				Version: 2,
   349  				Id:      "id",
   350  				Action:  "Something",
   351  			},
   352  			Version: 1,
   353  		},
   354  		body:   struct{ Arg string }{Arg: "an arg"},
   355  		expect: `{"request-id":1,"type":"Foo","version":2,"id":"id","request":"Something","params":{"Arg":"an arg"}}`,
   356  	}} {
   357  		c.Logf("test %d; %#v", i, test.hdr)
   358  		data := jsoncodec.DumpRequest(&test.hdr, test.body)
   359  		c.Check(string(data), gc.Equals, test.expect)
   360  	}
   361  }
   362  
   363  // assertJSONEqual compares the json strings v0
   364  // and v1 ignoring white space.
   365  func assertJSONEqual(c *gc.C, v0, v1 string) {
   366  	var m0, m1 interface{}
   367  	err := json.Unmarshal([]byte(v0), &m0)
   368  	c.Assert(err, jc.ErrorIsNil)
   369  	err = json.Unmarshal([]byte(v1), &m1)
   370  	c.Assert(err, jc.ErrorIsNil)
   371  	data0, err := json.Marshal(m0)
   372  	c.Assert(err, jc.ErrorIsNil)
   373  	data1, err := json.Marshal(m1)
   374  	c.Assert(err, jc.ErrorIsNil)
   375  	c.Assert(string(data0), gc.Equals, string(data1))
   376  }
   377  
   378  type testConn struct {
   379  	readMsgs  []string
   380  	err       error
   381  	writeMsgs []string
   382  	closed    bool
   383  }
   384  
   385  func (c *testConn) Receive(msg interface{}) error {
   386  	if len(c.readMsgs) > 0 {
   387  		s := c.readMsgs[0]
   388  		c.readMsgs = c.readMsgs[1:]
   389  		return json.Unmarshal([]byte(s), msg)
   390  	}
   391  	if c.err != nil {
   392  		return c.err
   393  	}
   394  	return io.EOF
   395  }
   396  
   397  func (c *testConn) Send(msg interface{}) error {
   398  	data, err := json.Marshal(msg)
   399  	if err != nil {
   400  		return err
   401  	}
   402  	c.writeMsgs = append(c.writeMsgs, string(data))
   403  	return nil
   404  }
   405  
   406  func (c *testConn) Close() error {
   407  	c.closed = true
   408  	return nil
   409  }