github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/api/logstream/logstream_test.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package logstream_test
     5  
     6  import (
     7  	"net/url"
     8  	"time"
     9  
    10  	"github.com/juju/errors"
    11  	"github.com/juju/loggo"
    12  	"github.com/juju/testing"
    13  	jc "github.com/juju/testing/checkers"
    14  	gc "gopkg.in/check.v1"
    15  
    16  	"github.com/juju/juju/api/base"
    17  	basetesting "github.com/juju/juju/api/base/testing"
    18  	"github.com/juju/juju/api/logstream"
    19  	"github.com/juju/juju/apiserver/params"
    20  	"github.com/juju/juju/logfwd"
    21  	coretesting "github.com/juju/juju/testing"
    22  	"github.com/juju/juju/version"
    23  )
    24  
    25  type LogReaderSuite struct {
    26  	coretesting.BaseSuite
    27  }
    28  
    29  var _ = gc.Suite(&LogReaderSuite{})
    30  
    31  func (s *LogReaderSuite) TestOpenFullConfig(c *gc.C) {
    32  	cUUID := "feebdaed-2f18-4fd2-967d-db9663db7bea"
    33  	stub := &testing.Stub{}
    34  	conn := &mockConnector{stub: stub}
    35  	stream := mockStream{stub: stub}
    36  	conn.ReturnConnectStream = stream
    37  	cfg := params.LogStreamConfig{
    38  		AllModels: true,
    39  		Sink:      "spam",
    40  	}
    41  
    42  	_, err := logstream.Open(conn, cfg, cUUID)
    43  	c.Assert(err, gc.IsNil)
    44  
    45  	stub.CheckCallNames(c, "ConnectStream")
    46  	stub.CheckCall(c, 0, "ConnectStream", `/logstream`, url.Values{
    47  		"all":  []string{"true"},
    48  		"sink": []string{"spam"},
    49  	})
    50  }
    51  
    52  func (s *LogReaderSuite) TestOpenError(c *gc.C) {
    53  	cUUID := "feebdaed-2f18-4fd2-967d-db9663db7bea"
    54  	stub := &testing.Stub{}
    55  	conn := &mockConnector{stub: stub}
    56  	failure := errors.New("foo")
    57  	stub.SetErrors(failure)
    58  	var cfg params.LogStreamConfig
    59  
    60  	_, err := logstream.Open(conn, cfg, cUUID)
    61  
    62  	c.Check(err, gc.ErrorMatches, "cannot connect to /logstream: foo")
    63  	stub.CheckCallNames(c, "ConnectStream")
    64  }
    65  
    66  func (s *LogReaderSuite) TestNextOneRecord(c *gc.C) {
    67  	ts := time.Now()
    68  	apiRec := params.LogStreamRecord{
    69  		ModelUUID: "deadbeef-2f18-4fd2-967d-db9663db7bea",
    70  		Entity:    "machine-99",
    71  		Version:   version.Current.String(),
    72  		Timestamp: ts,
    73  		Module:    "api.logstream.test",
    74  		Location:  "test.go:42",
    75  		Level:     loggo.INFO.String(),
    76  		Message:   "test message",
    77  	}
    78  	apiRecords := params.LogStreamRecords{
    79  		Records: []params.LogStreamRecord{apiRec},
    80  	}
    81  	cUUID := "feebdaed-2f18-4fd2-967d-db9663db7bea"
    82  	stub := &testing.Stub{}
    83  	conn := &mockConnector{stub: stub}
    84  	jsonReader := mockStream{stub: stub}
    85  	logsCh := make(chan params.LogStreamRecords, 1)
    86  	logsCh <- apiRecords
    87  	jsonReader.ReturnReadJSON = logsCh
    88  	conn.ReturnConnectStream = jsonReader
    89  	var cfg params.LogStreamConfig
    90  	stream, err := logstream.Open(conn, cfg, cUUID)
    91  	c.Assert(err, gc.IsNil)
    92  	stub.ResetCalls()
    93  
    94  	// Check the record we injected into the stream.
    95  	var records []logfwd.Record
    96  	done := make(chan struct{})
    97  	go func() {
    98  		records, err = stream.Next()
    99  		c.Assert(err, jc.ErrorIsNil)
   100  		close(done)
   101  	}()
   102  	select {
   103  	case <-done:
   104  	case <-time.After(coretesting.LongWait):
   105  		c.Errorf("timed out waiting for record")
   106  	}
   107  	c.Assert(records, gc.HasLen, 1)
   108  	c.Check(records[0], jc.DeepEquals, logfwd.Record{
   109  		Origin: logfwd.Origin{
   110  			ControllerUUID: cUUID,
   111  			ModelUUID:      "deadbeef-2f18-4fd2-967d-db9663db7bea",
   112  			Hostname:       "machine-99.deadbeef-2f18-4fd2-967d-db9663db7bea",
   113  			Type:           logfwd.OriginTypeMachine,
   114  			Name:           "99",
   115  			Software: logfwd.Software{
   116  				PrivateEnterpriseNumber: 28978,
   117  				Name:    "jujud-machine-agent",
   118  				Version: version.Current,
   119  			},
   120  		},
   121  		Timestamp: ts,
   122  		Level:     loggo.INFO,
   123  		Location: logfwd.SourceLocation{
   124  			Module:   "api.logstream.test",
   125  			Filename: "test.go",
   126  			Line:     42,
   127  		},
   128  		Message: "test message",
   129  	})
   130  	stub.CheckCallNames(c, "ReadJSON")
   131  
   132  	// Make sure we don't get extras.
   133  	done = make(chan struct{})
   134  	go func() {
   135  		records, err = stream.Next()
   136  		c.Assert(err, jc.ErrorIsNil)
   137  		close(done)
   138  	}()
   139  	select {
   140  	case <-done:
   141  		c.Errorf("got extra record: %#v", records)
   142  	case <-time.After(coretesting.ShortWait):
   143  	}
   144  }
   145  
   146  func (s *LogReaderSuite) TestNextError(c *gc.C) {
   147  	cUUID := "feebdaed-2f18-4fd2-967d-db9663db7bea"
   148  	stub := &testing.Stub{}
   149  	conn := &mockConnector{stub: stub}
   150  	jsonReader := mockStream{stub: stub}
   151  	conn.ReturnConnectStream = jsonReader
   152  	failure := errors.New("an error")
   153  	stub.SetErrors(nil, failure)
   154  	var cfg params.LogStreamConfig
   155  	stream, err := logstream.Open(conn, cfg, cUUID)
   156  	c.Assert(err, gc.IsNil)
   157  
   158  	var nextErr error
   159  	done := make(chan struct{})
   160  	go func() {
   161  		_, nextErr = stream.Next()
   162  		c.Check(errors.Cause(nextErr), gc.Equals, failure)
   163  		close(done)
   164  	}()
   165  	select {
   166  	case <-done:
   167  	case <-time.After(coretesting.LongWait):
   168  		c.Errorf("timed out waiting for record")
   169  	}
   170  	stub.CheckCallNames(c, "ConnectStream", "ReadJSON")
   171  }
   172  
   173  func (s *LogReaderSuite) TestClose(c *gc.C) {
   174  	cUUID := "feebdaed-2f18-4fd2-967d-db9663db7bea"
   175  	stub := &testing.Stub{}
   176  	conn := &mockConnector{stub: stub}
   177  	jsonReader := mockStream{stub: stub}
   178  	conn.ReturnConnectStream = jsonReader
   179  	var cfg params.LogStreamConfig
   180  	stream, err := logstream.Open(conn, cfg, cUUID)
   181  	c.Assert(err, gc.IsNil)
   182  	stub.ResetCalls()
   183  
   184  	err = stream.Close()
   185  	c.Assert(err, jc.ErrorIsNil)
   186  	err = stream.Close() // idempotent
   187  	c.Assert(err, jc.ErrorIsNil)
   188  
   189  	_, err = stream.Next()
   190  	c.Check(err, gc.ErrorMatches, `cannot read from closed stream`)
   191  	stub.CheckCallNames(c, "Close")
   192  }
   193  
   194  type mockConnector struct {
   195  	basetesting.APICallerFunc
   196  	stub *testing.Stub
   197  
   198  	ReturnConnectStream base.Stream
   199  }
   200  
   201  func (c *mockConnector) ConnectStream(path string, values url.Values) (base.Stream, error) {
   202  	c.stub.AddCall("ConnectStream", path, values)
   203  	if err := c.stub.NextErr(); err != nil {
   204  		return nil, errors.Trace(err)
   205  	}
   206  	return c.ReturnConnectStream, nil
   207  }
   208  
   209  type mockStream struct {
   210  	base.Stream
   211  	stub *testing.Stub
   212  
   213  	ReturnReadJSON chan params.LogStreamRecords
   214  }
   215  
   216  func (s mockStream) ReadJSON(v interface{}) error {
   217  	s.stub.AddCall("ReadJSON", v)
   218  	if err := s.stub.NextErr(); err != nil {
   219  		return errors.Trace(err)
   220  	}
   221  
   222  	switch vt := v.(type) {
   223  	case *params.LogStreamRecords:
   224  		*vt = <-s.ReturnReadJSON
   225  		return nil
   226  	default:
   227  		return errors.Errorf("unexpected output type: %T", v)
   228  	}
   229  }
   230  
   231  func (s mockStream) Close() error {
   232  	s.stub.AddCall("Close")
   233  	if err := s.stub.NextErr(); err != nil {
   234  		return errors.Trace(err)
   235  	}
   236  	return nil
   237  }