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