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 }