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(&params.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(&params.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  }