github.com/mongodb/grip@v0.0.0-20240213223901-f906268d82b9/logging/send_test.go (about) 1 package logging 2 3 import ( 4 "errors" 5 "fmt" 6 "os" 7 "os/exec" 8 "testing" 9 10 "github.com/mongodb/grip/level" 11 "github.com/mongodb/grip/message" 12 "github.com/mongodb/grip/send" 13 "github.com/stretchr/testify/suite" 14 ) 15 16 type GripInternalSuite struct { 17 grip *Grip 18 name string 19 suite.Suite 20 } 21 22 func TestGripSuite(t *testing.T) { 23 suite.Run(t, new(GripInternalSuite)) 24 } 25 26 func (s *GripInternalSuite) SetupSuite() { 27 s.name = "test" 28 s.grip = NewGrip(s.name) 29 s.Equal(s.grip.Name(), s.name) 30 } 31 32 func (s *GripInternalSuite) SetupTest() { 33 s.grip.SetName(s.name) 34 sender, err := send.NewNativeLogger(s.grip.Name(), send.LevelInfo{Default: level.Info, Threshold: level.Trace}) 35 s.NoError(err) 36 s.NoError(s.grip.SetSender(sender)) 37 } 38 39 func (s *GripInternalSuite) TestPanicSenderActuallyPanics() { 40 // both of these are in anonymous functions so that the defers 41 // cover the correct area. 42 43 func() { 44 // first make sure that the default send method doesn't panic 45 defer func() { 46 s.Nil(recover()) 47 }() 48 49 s.grip.GetSender().Send(message.NewLineMessage(level.Critical, "foo")) 50 }() 51 52 func() { 53 // call a panic function with a recoverer set. 54 defer func() { 55 s.NotNil(recover()) 56 }() 57 58 s.grip.sendPanic(message.NewLineMessage(level.Info, "foo")) 59 }() 60 } 61 62 func (s *GripInternalSuite) TestSetSenderErrorsForNil() { 63 s.Error(s.grip.SetSender(nil)) 64 } 65 66 func (s *GripInternalSuite) TestPanicSenderRespectsTThreshold() { 67 s.True(level.Debug > s.grip.GetSender().Level().Threshold) 68 s.NoError(s.grip.GetSender().SetLevel(send.LevelInfo{Default: level.Info, Threshold: level.Notice})) 69 s.True(level.Debug < s.grip.GetSender().Level().Threshold) 70 71 // test that there is a no panic if the message isn't "logabble" 72 defer func() { 73 s.Nil(recover()) 74 }() 75 76 s.grip.sendPanic(message.NewLineMessage(level.Debug, "foo")) 77 } 78 79 func (s *GripInternalSuite) TestConditionalSend() { 80 // because sink is an internal type (implementation of 81 // sender,) and "GetMessage" isn't in the interface, though it 82 // is exported, we can't pass the sink between functions. 83 sink, err := send.NewInternalLogger("sink", s.grip.GetSender().Level()) 84 s.NoError(err) 85 s.NoError(s.grip.SetSender(sink)) 86 87 msg := message.NewLineMessage(level.Info, "foo") 88 msgTwo := message.NewLineMessage(level.Notice, "bar") 89 90 // when the conditional argument is true, it should work 91 s.grip.Log(msg.Priority(), message.When(true, msg)) 92 s.Equal(msg.Raw(), sink.GetMessage().Message.Raw()) 93 94 // when the conditional argument is true, it should work, and the channel is fifo 95 s.grip.Log(msgTwo.Priority(), message.When(false, msgTwo)) 96 s.grip.Log(msg.Priority(), message.When(true, msg)) 97 result := sink.GetMessage().Message 98 if result.Loggable() { 99 s.Equal(msg.Raw(), result.Raw()) 100 } else { 101 s.Equal(msgTwo.Raw(), result.Raw()) 102 } 103 104 // change the order 105 s.grip.Log(msg.Priority(), message.When(true, msg)) 106 s.grip.Log(msgTwo.Priority(), message.When(false, msgTwo)) 107 result = sink.GetMessage().Message 108 109 if result.Loggable() { 110 s.Equal(msg.Raw(), result.Raw()) 111 } else { 112 s.Equal(msgTwo.Raw(), result.Raw()) 113 } 114 } 115 116 func (s *GripInternalSuite) TestCatchMethods() { 117 sink, err := send.NewInternalLogger("sink", send.LevelInfo{Default: level.Trace, Threshold: level.Trace}) 118 s.NoError(err) 119 s.NoError(s.grip.SetSender(sink)) 120 121 cases := []interface{}{ 122 s.grip.Alert, 123 s.grip.Critical, 124 s.grip.Debug, 125 s.grip.Emergency, 126 s.grip.Error, 127 s.grip.Info, 128 s.grip.Notice, 129 s.grip.Warning, 130 131 s.grip.Alertln, 132 s.grip.Criticalln, 133 s.grip.Debugln, 134 s.grip.Emergencyln, 135 s.grip.Errorln, 136 s.grip.Infoln, 137 s.grip.Noticeln, 138 s.grip.Warningln, 139 140 s.grip.Alertf, 141 s.grip.Criticalf, 142 s.grip.Debugf, 143 s.grip.Emergencyf, 144 s.grip.Errorf, 145 s.grip.Infof, 146 s.grip.Noticef, 147 s.grip.Warningf, 148 149 s.grip.AlertWhen, 150 s.grip.CriticalWhen, 151 s.grip.DebugWhen, 152 s.grip.EmergencyWhen, 153 s.grip.ErrorWhen, 154 s.grip.InfoWhen, 155 s.grip.NoticeWhen, 156 s.grip.WarningWhen, 157 158 s.grip.AlertWhenln, 159 s.grip.CriticalWhenln, 160 s.grip.DebugWhenln, 161 s.grip.EmergencyWhenln, 162 s.grip.ErrorWhenln, 163 s.grip.InfoWhenln, 164 s.grip.NoticeWhenln, 165 s.grip.WarningWhenln, 166 167 s.grip.AlertWhenf, 168 s.grip.CriticalWhenf, 169 s.grip.DebugWhenf, 170 s.grip.EmergencyWhenf, 171 s.grip.ErrorWhenf, 172 s.grip.InfoWhenf, 173 s.grip.NoticeWhenf, 174 s.grip.WarningWhenf, 175 176 func(w bool, m interface{}) { s.grip.LogWhen(w, level.Info, m) }, 177 func(w bool, m ...interface{}) { s.grip.LogWhenln(w, level.Info, m...) }, 178 func(w bool, m string, a ...interface{}) { s.grip.LogWhenf(w, level.Info, m, a...) }, 179 func(m interface{}) { s.grip.Log(level.Info, m) }, 180 func(m string, a ...interface{}) { s.grip.Logf(level.Info, m, a...) }, 181 func(m ...interface{}) { s.grip.Logln(level.Info, m...) }, 182 func(m ...message.Composer) { s.grip.Log(level.Info, m) }, 183 func(m []message.Composer) { s.grip.Log(level.Info, m) }, 184 func(w bool, m ...message.Composer) { s.grip.LogWhen(w, level.Info, m) }, 185 func(w bool, m []message.Composer) { s.grip.LogWhen(w, level.Info, m) }, 186 } 187 188 const msg = "hello world!" 189 multiMessage := []message.Composer{ 190 message.ConvertToComposer(0, nil), 191 message.ConvertToComposer(0, msg), 192 } 193 194 for _, logger := range cases { 195 s.Equal(0, sink.Len()) 196 s.False(sink.HasMessage()) 197 198 switch log := logger.(type) { 199 case func(error): 200 log(errors.New(msg)) 201 case func(interface{}): 202 log(msg) 203 case func(...interface{}): 204 log(msg, "", nil) 205 case func(string, ...interface{}): 206 log("%s", msg) 207 case func(bool, interface{}): 208 log(false, msg) 209 log(true, msg) 210 case func(bool, ...interface{}): 211 log(false, msg, "", nil) 212 log(true, msg, "", nil) 213 case func(bool, string, ...interface{}): 214 log(false, "%s", msg) 215 log(true, "%s", msg) 216 case func(...message.Composer): 217 log(multiMessage...) 218 case func(bool, ...message.Composer): 219 log(false, multiMessage...) 220 log(true, multiMessage...) 221 case func([]message.Composer): 222 log(multiMessage) 223 case func(bool, []message.Composer): 224 log(false, multiMessage) 225 log(true, multiMessage) 226 default: 227 panic(fmt.Sprintf("%T is not supported\n", log)) 228 } 229 230 if sink.Len() > 1 { 231 // this is the many case 232 var numLogged int 233 out := sink.GetMessage() 234 for i := 0; i < sink.Len(); i++ { 235 out = sink.GetMessage() 236 if out.Logged { 237 numLogged++ 238 s.Equal(out.Rendered, msg) 239 } 240 } 241 242 s.True(numLogged == 1, fmt.Sprintf("%T: %d %s", logger, numLogged, out.Priority)) 243 244 continue 245 } 246 247 s.True(sink.Len() == 1) 248 s.True(sink.HasMessage()) 249 out := sink.GetMessage() 250 s.Equal(out.Rendered, msg) 251 s.True(out.Logged, fmt.Sprintf("%T %s", logger, out.Priority)) 252 } 253 } 254 255 // This testing method uses the technique outlined in: 256 // http://stackoverflow.com/a/33404435 to test a function that exits 257 // since it's impossible to "catch" an os.Exit 258 func TestSendFatalExits(t *testing.T) { 259 grip := NewGrip("test") 260 if os.Getenv("SHOULD_CRASH") == "1" { 261 grip.sendFatal(message.NewLineMessage(level.Error, "foo")) 262 return 263 } 264 265 cmd := exec.Command(os.Args[0], "-test.run=TestSendFatalExits") 266 cmd.Env = append(os.Environ(), "SHOULD_CRASH=1") 267 err := cmd.Run() 268 if err == nil { 269 t.Errorf("sendFatal should have exited 0, instead: %+v", err) 270 } 271 }