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 }