github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/api/pubsub/pubsub_test.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package pubsub_test 5 6 import ( 7 "errors" 8 "io" 9 "net/url" 10 "time" 11 12 "github.com/juju/loggo" 13 "github.com/juju/pubsub" 14 jc "github.com/juju/testing/checkers" 15 gc "gopkg.in/check.v1" 16 "gopkg.in/juju/names.v2" 17 18 "github.com/juju/juju/api" 19 "github.com/juju/juju/api/base" 20 apipubsub "github.com/juju/juju/api/pubsub" 21 "github.com/juju/juju/apiserver/params" 22 "github.com/juju/juju/apiserver/testserver" 23 "github.com/juju/juju/state" 24 statetesting "github.com/juju/juju/state/testing" 25 coretesting "github.com/juju/juju/testing" 26 "github.com/juju/juju/testing/factory" 27 ) 28 29 type PubSubSuite struct { 30 coretesting.BaseSuite 31 } 32 33 var _ = gc.Suite(&PubSubSuite{}) 34 35 func (s *PubSubSuite) TestNewAPI(c *gc.C) { 36 conn := &mockConnector{ 37 c: c, 38 } 39 a := apipubsub.NewAPI(conn) 40 w, err := a.OpenMessageWriter() 41 c.Assert(err, gc.IsNil) 42 43 msg := new(params.PubSubMessage) 44 err = w.ForwardMessage(msg) 45 c.Assert(err, gc.IsNil) 46 47 c.Assert(conn.written, gc.HasLen, 1) 48 c.Assert(conn.written[0], gc.Equals, msg) 49 50 err = w.Close() 51 c.Assert(err, gc.IsNil) 52 c.Assert(conn.closeCount, gc.Equals, 1) 53 } 54 55 func (s *PubSubSuite) TestNewAPIWriteLogError(c *gc.C) { 56 conn := &mockConnector{ 57 c: c, 58 connectError: errors.New("foo"), 59 } 60 a := apipubsub.NewAPI(conn) 61 w, err := a.OpenMessageWriter() 62 c.Assert(err, gc.ErrorMatches, "cannot connect to /pubsub: foo") 63 c.Assert(w, gc.Equals, nil) 64 } 65 66 func (s *PubSubSuite) TestNewAPIWriteError(c *gc.C) { 67 conn := &mockConnector{ 68 c: c, 69 writeError: errors.New("foo"), 70 } 71 a := apipubsub.NewAPI(conn) 72 w, err := a.OpenMessageWriter() 73 c.Assert(err, gc.IsNil) 74 defer w.Close() 75 76 err = w.ForwardMessage(new(params.PubSubMessage)) 77 c.Assert(err, gc.ErrorMatches, "cannot send pubsub message: foo") 78 c.Assert(conn.written, gc.HasLen, 0) 79 } 80 81 type mockConnector struct { 82 c *gc.C 83 84 connectError error 85 writeError error 86 written []interface{} 87 88 closeCount int 89 } 90 91 func (c *mockConnector) ConnectStream(path string, values url.Values) (base.Stream, error) { 92 c.c.Assert(path, gc.Equals, "/pubsub") 93 c.c.Assert(values, gc.HasLen, 0) 94 if c.connectError != nil { 95 return nil, c.connectError 96 } 97 return mockStream{c}, nil 98 } 99 100 type mockStream struct { 101 conn *mockConnector 102 } 103 104 func (s mockStream) WriteJSON(v interface{}) error { 105 if s.conn.writeError != nil { 106 return s.conn.writeError 107 } 108 s.conn.written = append(s.conn.written, v) 109 return nil 110 } 111 112 func (s mockStream) ReadJSON(v interface{}) error { 113 s.conn.c.Errorf("ReadJSON called unexpectedly") 114 return nil 115 } 116 117 func (s mockStream) NextReader() (messageType int, r io.Reader, err error) { 118 // NextReader is now called by the read loop thread. 119 // So just wait a bit and return so it doesn't sit in a very tight loop. 120 time.Sleep(time.Millisecond) 121 return 0, nil, nil 122 } 123 124 func (s mockStream) Close() error { 125 s.conn.closeCount++ 126 return nil 127 } 128 129 type PubSubIntegrationSuite struct { 130 statetesting.StateSuite 131 machineTag names.Tag 132 password string 133 nonce string 134 hub *pubsub.StructuredHub 135 info *api.Info 136 } 137 138 var _ = gc.Suite(&PubSubIntegrationSuite{}) 139 140 func (s *PubSubIntegrationSuite) SetUpTest(c *gc.C) { 141 s.StateSuite.SetUpTest(c) 142 loggo.GetLogger("juju.apiserver").SetLogLevel(loggo.TRACE) 143 s.nonce = "nonce" 144 m, password := s.Factory.MakeMachineReturningPassword(c, &factory.MachineParams{ 145 Nonce: s.nonce, 146 Jobs: []state.MachineJob{state.JobManageModel}, 147 }) 148 s.machineTag = m.Tag() 149 s.password = password 150 s.hub = pubsub.NewStructuredHub(nil) 151 152 config := testserver.DefaultServerConfig(c) 153 config.Hub = s.hub 154 server := testserver.NewServerWithConfig(c, s.StatePool, config) 155 s.AddCleanup(func(c *gc.C) { c.Assert(server.Stop(), jc.ErrorIsNil) }) 156 157 s.info = server.Info 158 s.info.ModelTag = s.Model.ModelTag() 159 s.info.Tag = s.machineTag 160 s.info.Password = s.password 161 s.info.Nonce = s.nonce 162 } 163 164 func (s *PubSubIntegrationSuite) connect(c *gc.C) apipubsub.MessageWriter { 165 conn, err := api.Open(s.info, api.DialOpts{}) 166 c.Assert(err, jc.ErrorIsNil) 167 s.AddCleanup(func(_ *gc.C) { conn.Close() }) 168 169 a := apipubsub.NewAPI(conn) 170 w, err := a.OpenMessageWriter() 171 c.Assert(err, jc.ErrorIsNil) 172 s.AddCleanup(func(_ *gc.C) { w.Close() }) 173 return w 174 } 175 176 func (s *PubSubIntegrationSuite) TestMessages(c *gc.C) { 177 writer := s.connect(c) 178 topic := "test.message" 179 messages := []map[string]interface{}{} 180 done := make(chan struct{}) 181 _, err := s.hub.SubscribeMatch(pubsub.MatchAll, func(t string, payload map[string]interface{}) { 182 c.Check(t, gc.Equals, topic) 183 messages = append(messages, payload) 184 if len(messages) == 2 { 185 close(done) 186 } 187 }) 188 189 first := map[string]interface{}{ 190 "key": "value", 191 } 192 err = writer.ForwardMessage(¶ms.PubSubMessage{ 193 Topic: string(topic), 194 Data: first, 195 }) 196 c.Assert(err, jc.ErrorIsNil) 197 198 second := map[string]interface{}{ 199 "key": "other", 200 } 201 err = writer.ForwardMessage(¶ms.PubSubMessage{ 202 Topic: string(topic), 203 Data: second, 204 }) 205 c.Assert(err, jc.ErrorIsNil) 206 207 select { 208 case <-done: 209 // messages received 210 case <-time.After(coretesting.LongWait): 211 c.Fatal("messages not received") 212 } 213 c.Assert(messages, jc.DeepEquals, []map[string]interface{}{first, second}) 214 }