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 }