func commonTestOrderedCalls(t *testing.T) (reporter *ErrorReporter, ctrl *gomock.Controller, subjectOne, subjectTwo *Subject) { reporter, ctrl = createFixtures(t) subjectOne = new(Subject) subjectTwo = new(Subject) gomock.InOrder( ctrl.RecordCall(subjectOne, "FooMethod", "1").AnyTimes(), ctrl.RecordCall(subjectTwo, "FooMethod", "2"), ctrl.RecordCall(subjectTwo, "BarMethod", "3"), ) return }
func TestSocketListenConfig(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() mckLogger := NewMockLogger(mockCtrl) mckLogger.EXPECT().ShouldLog(gomock.Any()).Return(true).AnyTimes() mckLogger.EXPECT().Log(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() mckStat := NewMockStatistician(mockCtrl) mckListenerConfig := NewMockListenerConfig(mockCtrl) app := NewApplication() app.hostname = "example.org" app.SetLogger(mckLogger) app.SetMetrics(mckStat) sh := NewSocketHandler() sh.setApp(app) // Should forward Listen errors. listenErr := errors.New("splines not reticulated") mckListenerConfig.EXPECT().Listen().Return(nil, listenErr) if err := sh.listenWithConfig(mckListenerConfig); err != listenErr { t.Errorf("Wrong error: got %#v; want %#v", err, listenErr) } // Should use the wss:// scheme if UseTLS returns true. ml := newMockListener(netAddr{"test", "[::1]:8080"}) gomock.InOrder( mckListenerConfig.EXPECT().Listen().Return(ml, nil), mckListenerConfig.EXPECT().UseTLS().Return(true), mckListenerConfig.EXPECT().GetMaxConns().Return(1), ) if err := sh.listenWithConfig(mckListenerConfig); err != nil { t.Errorf("Error setting listener: %s", err) } if maxConns := sh.MaxConns(); maxConns != 1 { t.Errorf("Mismatched maximum connection count: got %d; want 1", maxConns) } expectedURL := "wss://example.org:8080" if url := sh.URL(); url != expectedURL { t.Errorf("Mismatched handler URL: got %q; want %q", url, expectedURL) } }
func TestCallAfterLoopPanic(t *testing.T) { _, ctrl := createFixtures(t) subject := new(Subject) firstCall := ctrl.RecordCall(subject, "Foo", "1") secondCall := ctrl.RecordCall(subject, "Foo", "2") thirdCall := ctrl.RecordCall(subject, "Foo", "3") gomock.InOrder(firstCall, secondCall, thirdCall) defer func() { err := recover() if err == nil { t.Error("Call.After creation of dependency loop did not panic.") } }() // This should panic due to dependency loop. firstCall.After(thirdCall) }
func DecoderSpec(c gs.Context) { t := new(ts.SimpleT) ctrl := gomock.NewController(t) defer ctrl.Finish() pConfig := pipeline.NewPipelineConfig(nil) c.Specify("A SandboxDecoder", func() { decoder := new(SandboxDecoder) decoder.SetPipelineConfig(pConfig) conf := decoder.ConfigStruct().(*sandbox.SandboxConfig) supply := make(chan *pipeline.PipelinePack, 1) pack := pipeline.NewPipelinePack(supply) dRunner := pm.NewMockDecoderRunner(ctrl) c.Specify("that uses lpeg and inject_message", func() { dRunner.EXPECT().Name().Return("serialize") conf.ScriptFilename = "../lua/testsupport/decoder.lua" err := decoder.Init(conf) c.Assume(err, gs.IsNil) c.Specify("decodes simple messages", func() { data := "1376389920 debug id=2321 url=example.com item=1" decoder.SetDecoderRunner(dRunner) pack.Message.SetPayload(data) _, err = decoder.Decode(pack) c.Assume(err, gs.IsNil) c.Expect(pack.Message.GetTimestamp(), gs.Equals, int64(1376389920000000000)) c.Expect(pack.Message.GetSeverity(), gs.Equals, int32(7)) var ok bool var value interface{} value, ok = pack.Message.GetFieldValue("id") c.Expect(ok, gs.Equals, true) c.Expect(value, gs.Equals, "2321") value, ok = pack.Message.GetFieldValue("url") c.Expect(ok, gs.Equals, true) c.Expect(value, gs.Equals, "example.com") value, ok = pack.Message.GetFieldValue("item") c.Expect(ok, gs.Equals, true) c.Expect(value, gs.Equals, "1") decoder.Shutdown() }) c.Specify("decodes an invalid messages", func() { data := "1376389920 bogus id=2321 url=example.com item=1" decoder.SetDecoderRunner(dRunner) pack.Message.SetPayload(data) packs, err := decoder.Decode(pack) c.Expect(len(packs), gs.Equals, 0) c.Expect(err.Error(), gs.Equals, "Failed parsing: "+data) c.Expect(decoder.processMessageFailures, gs.Equals, int64(1)) decoder.Shutdown() }) c.Specify("Preserves data", func() { conf.ScriptFilename = "../lua/testsupport/serialize.lua" conf.PreserveData = true err := decoder.Init(conf) c.Assume(err, gs.IsNil) decoder.SetDecoderRunner(dRunner) decoder.Shutdown() _, err = os.Stat("sandbox_preservation/serialize.data") c.Expect(err, gs.IsNil) err = os.Remove("sandbox_preservation/serialize.data") c.Expect(err, gs.IsNil) }) }) c.Specify("that only uses write_message", func() { conf.ScriptFilename = "../lua/testsupport/write_message_decoder.lua" dRunner.EXPECT().Name().Return("write_message") err := decoder.Init(conf) decoder.SetDecoderRunner(dRunner) c.Assume(err, gs.IsNil) c.Specify("adds a string field to the message", func() { data := "string field scribble" pack.Message.SetPayload(data) packs, err := decoder.Decode(pack) c.Expect(err, gs.IsNil) c.Expect(len(packs), gs.Equals, 1) c.Expect(packs[0], gs.Equals, pack) value, ok := pack.Message.GetFieldValue("scribble") c.Expect(ok, gs.IsTrue) c.Expect(value.(string), gs.Equals, "foo") }) c.Specify("adds a numeric field to the message", func() { data := "num field scribble" pack.Message.SetPayload(data) packs, err := decoder.Decode(pack) c.Expect(err, gs.IsNil) c.Expect(len(packs), gs.Equals, 1) c.Expect(packs[0], gs.Equals, pack) value, ok := pack.Message.GetFieldValue("scribble") c.Expect(ok, gs.IsTrue) c.Expect(value.(float64), gs.Equals, float64(1)) }) c.Specify("adds a boolean field to the message", func() { data := "bool field scribble" pack.Message.SetPayload(data) packs, err := decoder.Decode(pack) c.Expect(err, gs.IsNil) c.Expect(len(packs), gs.Equals, 1) c.Expect(packs[0], gs.Equals, pack) value, ok := pack.Message.GetFieldValue("scribble") c.Expect(ok, gs.IsTrue) c.Expect(value.(bool), gs.Equals, true) }) c.Specify("sets type and payload", func() { data := "set type and payload" pack.Message.SetPayload(data) packs, err := decoder.Decode(pack) c.Expect(err, gs.IsNil) c.Expect(len(packs), gs.Equals, 1) c.Expect(packs[0], gs.Equals, pack) c.Expect(pack.Message.GetType(), gs.Equals, "my_type") c.Expect(pack.Message.GetPayload(), gs.Equals, "my_payload") }) c.Specify("sets field value with representation", func() { data := "set field value with representation" pack.Message.SetPayload(data) packs, err := decoder.Decode(pack) c.Expect(err, gs.IsNil) c.Expect(len(packs), gs.Equals, 1) c.Expect(packs[0], gs.Equals, pack) fields := pack.Message.FindAllFields("rep") c.Expect(len(fields), gs.Equals, 1) field := fields[0] values := field.GetValueString() c.Expect(len(values), gs.Equals, 1) c.Expect(values[0], gs.Equals, "foo") c.Expect(field.GetRepresentation(), gs.Equals, "representation") }) c.Specify("sets multiple field string values", func() { data := "set multiple field string values" pack.Message.SetPayload(data) packs, err := decoder.Decode(pack) c.Expect(err, gs.IsNil) c.Expect(len(packs), gs.Equals, 1) c.Expect(packs[0], gs.Equals, pack) fields := pack.Message.FindAllFields("multi") c.Expect(len(fields), gs.Equals, 2) values := fields[0].GetValueString() c.Expect(len(values), gs.Equals, 1) c.Expect(values[0], gs.Equals, "first") values = fields[1].GetValueString() c.Expect(len(values), gs.Equals, 1) c.Expect(values[0], gs.Equals, "second") }) c.Specify("sets field string array value", func() { data := "set field string array value" pack.Message.SetPayload(data) packs, err := decoder.Decode(pack) c.Expect(err, gs.IsNil) c.Expect(len(packs), gs.Equals, 1) c.Expect(packs[0], gs.Equals, pack) fields := pack.Message.FindAllFields("array") c.Expect(len(fields), gs.Equals, 1) values := fields[0].GetValueString() c.Expect(len(values), gs.Equals, 2) c.Expect(values[0], gs.Equals, "first") c.Expect(values[1], gs.Equals, "second") }) }) }) c.Specify("A Multipack SandboxDecoder", func() { decoder := new(SandboxDecoder) decoder.SetPipelineConfig(pConfig) conf := decoder.ConfigStruct().(*sandbox.SandboxConfig) conf.ScriptFilename = "../lua/testsupport/multipack_decoder.lua" supply := make(chan *pipeline.PipelinePack, 3) pack := pipeline.NewPipelinePack(supply) pack.Message = getTestMessage() pack1 := pipeline.NewPipelinePack(supply) pack2 := pipeline.NewPipelinePack(supply) dRunner := pm.NewMockDecoderRunner(ctrl) dRunner.EXPECT().Name().Return("SandboxDecoder") c.Specify("decodes into multiple packs", func() { err := decoder.Init(conf) c.Assume(err, gs.IsNil) decoder.SetDecoderRunner(dRunner) gomock.InOrder( dRunner.EXPECT().NewPack().Return(pack1), dRunner.EXPECT().NewPack().Return(pack2), ) packs, err := decoder.Decode(pack) c.Expect(len(packs), gs.Equals, 3) c.Expect(packs[0].Message.GetPayload(), gs.Equals, "message one") c.Expect(packs[1].Message.GetPayload(), gs.Equals, "message two") c.Expect(packs[2].Message.GetPayload(), gs.Equals, "message three") for i := 0; i < 1; i++ { c.Expect(packs[i].Message.GetType(), gs.Equals, "TEST") c.Expect(packs[i].Message.GetHostname(), gs.Equals, "my.host.name") c.Expect(packs[i].Message.GetLogger(), gs.Equals, "GoSpec") c.Expect(packs[i].Message.GetSeverity(), gs.Equals, int32(6)) } decoder.Shutdown() }) }) c.Specify("Linux Cpu Stats decoder", func() { decoder := new(SandboxDecoder) decoder.SetPipelineConfig(pConfig) conf := decoder.ConfigStruct().(*sandbox.SandboxConfig) conf.ScriptFilename = "../lua/decoders/linux_loadavg.lua" conf.ModuleDirectory = "../../../../../../modules" conf.MemoryLimit = 8e6 conf.Config = make(map[string]interface{}) supply := make(chan *pipeline.PipelinePack, 1) pack := pipeline.NewPipelinePack(supply) dRunner := pm.NewMockDecoderRunner(ctrl) dRunner.EXPECT().Name().Return("SandboxDecoder") err := decoder.Init(conf) c.Assume(err, gs.IsNil) decoder.SetDecoderRunner(dRunner) c.Specify("decodes a message", func() { payload := "0.00 0.01 0.05 3/153 660\n" pack.Message.SetPayload(payload) f, err := message.NewField("FilePath", "/proc/loadavg", "") c.Assume(err, gs.IsNil) pack.Message.AddField(f) _, err = decoder.Decode(pack) c.Assume(err, gs.IsNil) c.Expect(pack.Message.GetSeverity(), gs.Equals, int32(7)) var ok bool var value interface{} value, ok = pack.Message.GetFieldValue("1MinAvg") c.Expect(ok, gs.IsTrue) c.Expect(value, gs.Equals, 0.00) value, ok = pack.Message.GetFieldValue("5MinAvg") c.Expect(ok, gs.IsTrue) c.Expect(value, gs.Equals, 0.01) value, ok = pack.Message.GetFieldValue("15MinAvg") c.Expect(ok, gs.IsTrue) c.Expect(value, gs.Equals, 0.05) value, ok = pack.Message.GetFieldValue("NumProcesses") c.Expect(ok, gs.IsTrue) c.Expect(value, gs.Equals, float64(3)) value, ok = pack.Message.GetFieldValue("FilePath") c.Expect(ok, gs.IsTrue) c.Expect(value, gs.Equals, "/proc/loadavg") }) c.Specify("decodes an invalid message", func() { data := "bogus message" pack.Message.SetPayload(data) packs, err := decoder.Decode(pack) c.Expect(len(packs), gs.Equals, 0) c.Expect(err.Error(), gs.Equals, "Failed parsing: "+data) c.Expect(decoder.processMessageFailures, gs.Equals, int64(1)) decoder.Shutdown() }) }) c.Specify("Linux Mem Stats decoder", func() { decoder := new(SandboxDecoder) decoder.SetPipelineConfig(pConfig) conf := decoder.ConfigStruct().(*sandbox.SandboxConfig) conf.ScriptFilename = "../lua/decoders/linux_memstats.lua" conf.ModuleDirectory = "../../../../../../modules" conf.MemoryLimit = 8e6 conf.Config = make(map[string]interface{}) supply := make(chan *pipeline.PipelinePack, 1) pack := pipeline.NewPipelinePack(supply) dRunner := pm.NewMockDecoderRunner(ctrl) dRunner.EXPECT().Name().Return("SandboxDecoder") err := decoder.Init(conf) c.Assume(err, gs.IsNil) decoder.SetDecoderRunner(dRunner) c.Specify("decodes a message", func() { payload := `MemTotal: 4047616 kB MemFree: 3135780 kB HugePages_Free: 0 ` pack.Message.SetPayload(payload) f, err := message.NewField("FilePath", "/proc/meminfo", "") c.Assume(err, gs.IsNil) pack.Message.AddField(f) _, err = decoder.Decode(pack) c.Assume(err, gs.IsNil) c.Expect(pack.Message.GetSeverity(), gs.Equals, int32(7)) var ok bool var value interface{} value, ok = pack.Message.GetFieldValue("MemTotal") c.Expect(ok, gs.IsTrue) c.Expect(value, gs.Equals, 4.047616e+06) value, ok = pack.Message.GetFieldValue("MemFree") c.Expect(ok, gs.IsTrue) c.Expect(value, gs.Equals, 3.13578e+06) value, ok = pack.Message.GetFieldValue("HugePages_Free") c.Expect(ok, gs.IsTrue) c.Expect(value, gs.Equals, float64(0)) value, ok = pack.Message.GetFieldValue("FilePath") c.Expect(ok, gs.IsTrue) c.Expect(value, gs.Equals, "/proc/meminfo") }) c.Specify("decodes an invalid message", func() { data := "bogus message" pack.Message.SetPayload(data) packs, err := decoder.Decode(pack) c.Expect(len(packs), gs.Equals, 0) c.Expect(err.Error(), gs.Equals, "Failed parsing: "+data) c.Expect(decoder.processMessageFailures, gs.Equals, int64(1)) decoder.Shutdown() }) }) c.Specify("Linux Disk Stats decoder", func() { decoder := new(SandboxDecoder) decoder.SetPipelineConfig(pConfig) conf := decoder.ConfigStruct().(*sandbox.SandboxConfig) conf.ScriptFilename = "../lua/decoders/linux_diskstats.lua" conf.ModuleDirectory = "../../../../../../modules" conf.MemoryLimit = 8e6 conf.Config = make(map[string]interface{}) supply := make(chan *pipeline.PipelinePack, 1) pack := pipeline.NewPipelinePack(supply) dRunner := pm.NewMockDecoderRunner(ctrl) dRunner.EXPECT().Name().Return("SandboxDecoder") err := decoder.Init(conf) c.Assume(err, gs.IsNil) decoder.SetDecoderRunner(dRunner) c.Specify("decodes a message", func() { payload := " 13903 11393 969224 49444 10780 10161 1511920 4104 0 5064 53468\n" pack.Message.SetPayload(payload) f, err := message.NewField("FilePath", "/sys/block/sda/stat", "") c.Assume(err, gs.IsNil) pack.Message.AddField(f) f, err = message.NewField("TickerInterval", int64(2), "") c.Assume(err, gs.IsNil) pack.Message.AddField(f) _, err = decoder.Decode(pack) c.Assume(err, gs.IsNil) c.Expect(pack.Message.GetSeverity(), gs.Equals, int32(7)) var ok bool var value interface{} // These are in the same order the payload should be value, ok = pack.Message.GetFieldValue("ReadsCompleted") c.Expect(ok, gs.IsTrue) c.Expect(value, gs.Equals, float64(13903)) value, ok = pack.Message.GetFieldValue("ReadsMerged") c.Expect(ok, gs.IsTrue) c.Expect(value, gs.Equals, float64(11393)) value, ok = pack.Message.GetFieldValue("SectorsRead") c.Expect(ok, gs.IsTrue) c.Expect(value, gs.Equals, float64(969224)) value, ok = pack.Message.GetFieldValue("TimeReading") c.Expect(ok, gs.IsTrue) c.Expect(value, gs.Equals, float64(49444)) value, ok = pack.Message.GetFieldValue("WritesCompleted") c.Expect(ok, gs.IsTrue) c.Expect(value, gs.Equals, float64(10780)) value, ok = pack.Message.GetFieldValue("WritesMerged") c.Expect(ok, gs.IsTrue) c.Expect(value, gs.Equals, float64(10161)) value, ok = pack.Message.GetFieldValue("SectorsWritten") c.Expect(ok, gs.IsTrue) c.Expect(value, gs.Equals, float64(1511920)) value, ok = pack.Message.GetFieldValue("TimeWriting") c.Expect(ok, gs.IsTrue) c.Expect(value, gs.Equals, float64(4104)) value, ok = pack.Message.GetFieldValue("NumIOInProgress") c.Expect(ok, gs.IsTrue) c.Expect(value, gs.Equals, float64(0)) value, ok = pack.Message.GetFieldValue("TimeDoingIO") c.Expect(ok, gs.IsTrue) c.Expect(value, gs.Equals, float64(5064)) value, ok = pack.Message.GetFieldValue("WeightedTimeDoingIO") c.Expect(ok, gs.IsTrue) c.Expect(value, gs.Equals, float64(53468)) value, ok = pack.Message.GetFieldValue("TickerInterval") c.Expect(ok, gs.IsTrue) c.Expect(value, gs.Equals, float64(2)) value, ok = pack.Message.GetFieldValue("FilePath") c.Expect(ok, gs.IsTrue) c.Expect(value, gs.Equals, "/sys/block/sda/stat") }) c.Specify("decodes a message with no leading space", func() { payload := "19092852 0 510563170 15817012 46452019 0 1546950712 262535124 0 23823976 278362684\n" pack.Message.SetPayload(payload) _, err = decoder.Decode(pack) c.Assume(err, gs.IsNil) value, ok := pack.Message.GetFieldValue("ReadsCompleted") c.Expect(ok, gs.IsTrue) c.Expect(value, gs.Equals, float64(19092852)) }) c.Specify("decodes an invalid message", func() { data := "bogus message" pack.Message.SetPayload(data) packs, err := decoder.Decode(pack) c.Expect(len(packs), gs.Equals, 0) c.Expect(err.Error(), gs.Equals, "Failed parsing: "+data) c.Expect(decoder.processMessageFailures, gs.Equals, int64(1)) decoder.Shutdown() }) }) c.Specify("Nginx access log decoder", func() { decoder := new(SandboxDecoder) decoder.SetPipelineConfig(pConfig) conf := decoder.ConfigStruct().(*sandbox.SandboxConfig) conf.ScriptFilename = "../lua/decoders/nginx_access.lua" conf.ModuleDirectory = "../../../../../../modules" conf.MemoryLimit = 8e6 conf.Config = make(map[string]interface{}) conf.Config["log_format"] = "$remote_addr - $remote_user [$time_local] \"$request\" $status $body_bytes_sent \"$http_referer\" \"$http_user_agent\"" conf.Config["user_agent_transform"] = true supply := make(chan *pipeline.PipelinePack, 1) pack := pipeline.NewPipelinePack(supply) dRunner := pm.NewMockDecoderRunner(ctrl) dRunner.EXPECT().Name().Return("SandboxDecoder") err := decoder.Init(conf) c.Assume(err, gs.IsNil) decoder.SetDecoderRunner(dRunner) c.Specify("decodes simple messages", func() { data := "127.0.0.1 - - [10/Feb/2014:08:46:41 -0800] \"GET / HTTP/1.1\" 304 0 \"-\" \"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:26.0) Gecko/20100101 Firefox/26.0\"" pack.Message.SetPayload(data) _, err = decoder.Decode(pack) c.Assume(err, gs.IsNil) c.Expect(pack.Message.GetTimestamp(), gs.Equals, int64(1392050801000000000)) c.Expect(pack.Message.GetSeverity(), gs.Equals, int32(7)) var ok bool var value interface{} value, ok = pack.Message.GetFieldValue("remote_addr") c.Expect(ok, gs.Equals, true) c.Expect(value, gs.Equals, "127.0.0.1") value, ok = pack.Message.GetFieldValue("user_agent_browser") c.Expect(ok, gs.Equals, true) c.Expect(value, gs.Equals, "Firefox") value, ok = pack.Message.GetFieldValue("user_agent_version") c.Expect(ok, gs.Equals, true) c.Expect(value, gs.Equals, float64(26)) value, ok = pack.Message.GetFieldValue("user_agent_os") c.Expect(ok, gs.Equals, true) c.Expect(value, gs.Equals, "Linux") _, ok = pack.Message.GetFieldValue("http_user_agent") c.Expect(ok, gs.Equals, false) value, ok = pack.Message.GetFieldValue("body_bytes_sent") c.Expect(ok, gs.Equals, true) c.Expect(value, gs.Equals, float64(0)) value, ok = pack.Message.GetFieldValue("status") c.Expect(ok, gs.Equals, true) c.Expect(value, gs.Equals, float64(304)) decoder.Shutdown() }) c.Specify("decodes an invalid messages", func() { data := "bogus message" pack.Message.SetPayload(data) packs, err := decoder.Decode(pack) c.Expect(len(packs), gs.Equals, 0) c.Expect(err.Error(), gs.Equals, "Failed parsing: "+data) c.Expect(decoder.processMessageFailures, gs.Equals, int64(1)) decoder.Shutdown() }) }) c.Specify("Apache access log decoder", func() { decoder := new(SandboxDecoder) decoder.SetPipelineConfig(pConfig) conf := decoder.ConfigStruct().(*sandbox.SandboxConfig) conf.ScriptFilename = "../lua/decoders/apache_access.lua" conf.ModuleDirectory = "../../../../../../modules" conf.MemoryLimit = 8e6 conf.Config = make(map[string]interface{}) conf.Config["log_format"] = "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" conf.Config["user_agent_transform"] = true supply := make(chan *pipeline.PipelinePack, 1) pack := pipeline.NewPipelinePack(supply) dRunner := pm.NewMockDecoderRunner(ctrl) dRunner.EXPECT().Name().Return("SandboxDecoder") err := decoder.Init(conf) c.Assume(err, gs.IsNil) decoder.SetDecoderRunner(dRunner) c.Specify("decodes simple messages", func() { data := "127.0.0.1 - - [10/Feb/2014:08:46:41 -0800] \"GET / HTTP/1.1\" 304 0 \"-\" \"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:26.0) Gecko/20100101 Firefox/26.0\"" pack.Message.SetPayload(data) _, err = decoder.Decode(pack) c.Assume(err, gs.IsNil) c.Expect(pack.Message.GetTimestamp(), gs.Equals, int64(1392050801000000000)) c.Expect(pack.Message.GetSeverity(), gs.Equals, int32(7)) var ok bool var value interface{} value, ok = pack.Message.GetFieldValue("remote_addr") c.Expect(ok, gs.Equals, true) c.Expect(value, gs.Equals, "127.0.0.1") value, ok = pack.Message.GetFieldValue("user_agent_browser") c.Expect(ok, gs.Equals, true) c.Expect(value, gs.Equals, "Firefox") value, ok = pack.Message.GetFieldValue("user_agent_version") c.Expect(ok, gs.Equals, true) c.Expect(value, gs.Equals, float64(26)) value, ok = pack.Message.GetFieldValue("user_agent_os") c.Expect(ok, gs.Equals, true) c.Expect(value, gs.Equals, "Linux") _, ok = pack.Message.GetFieldValue("http_user_agent") c.Expect(ok, gs.Equals, false) value, ok = pack.Message.GetFieldValue("body_bytes_sent") c.Expect(ok, gs.Equals, true) c.Expect(value, gs.Equals, float64(0)) value, ok = pack.Message.GetFieldValue("status") c.Expect(ok, gs.Equals, true) c.Expect(value, gs.Equals, float64(304)) decoder.Shutdown() }) c.Specify("decodes an invalid messages", func() { data := "bogus message" pack.Message.SetPayload(data) packs, err := decoder.Decode(pack) c.Expect(len(packs), gs.Equals, 0) c.Expect(err.Error(), gs.Equals, "Failed parsing: "+data) c.Expect(decoder.processMessageFailures, gs.Equals, int64(1)) decoder.Shutdown() }) }) c.Specify("rsyslog decoder", func() { decoder := new(SandboxDecoder) decoder.SetPipelineConfig(pConfig) conf := decoder.ConfigStruct().(*sandbox.SandboxConfig) conf.ScriptFilename = "../lua/decoders/rsyslog.lua" conf.ModuleDirectory = "../../../../../../modules" conf.MemoryLimit = 8e6 conf.Config = make(map[string]interface{}) conf.Config["type"] = "MyTestFormat" conf.Config["template"] = "%pri% %TIMESTAMP% %TIMEGENERATED:::date-rfc3339% %HOSTNAME% %syslogtag%%msg:::sp-if-no-1st-sp%%msg:::drop-last-lf%\n" conf.Config["tz"] = "America/Los_Angeles" supply := make(chan *pipeline.PipelinePack, 1) pack := pipeline.NewPipelinePack(supply) dRunner := pm.NewMockDecoderRunner(ctrl) dRunner.EXPECT().Name().Return("SandboxDecoder") err := decoder.Init(conf) c.Assume(err, gs.IsNil) decoder.SetDecoderRunner(dRunner) c.Specify("decodes simple messages", func() { data := "28 Feb 10 12:58:58 2014-02-10T12:58:59-08:00 testhost widget[4322]: test message.\n" pack.Message.SetPayload(data) _, err = decoder.Decode(pack) c.Assume(err, gs.IsNil) // Syslog timestamp doesn't support year, so we have to calculate // it for the current year or else this test will fail every // January. year := time.Now().Year() tStr := fmt.Sprintf("%d Feb 10 12:58:58 -0800", year) t, err := time.Parse("2006 Jan 02 15:04:05 -0700", tStr) c.Assume(err, gs.IsNil) unixT := t.UnixNano() c.Expect(pack.Message.GetTimestamp(), gs.Equals, unixT) c.Expect(pack.Message.GetSeverity(), gs.Equals, int32(4)) c.Expect(pack.Message.GetHostname(), gs.Equals, "testhost") c.Expect(pack.Message.GetPid(), gs.Equals, int32(4322)) c.Expect(pack.Message.GetPayload(), gs.Equals, "test message.") c.Expect(pack.Message.GetType(), gs.Equals, conf.Config["type"]) var ok bool var value interface{} value, ok = pack.Message.GetFieldValue("programname") c.Expect(ok, gs.Equals, true) c.Expect(value, gs.Equals, "widget") value, ok = pack.Message.GetFieldValue("syslogfacility") c.Expect(ok, gs.Equals, true) c.Expect(value, gs.Equals, float64(3)) value, ok = pack.Message.GetFieldValue("timegenerated") c.Expect(ok, gs.Equals, true) c.Expect(value, gs.Equals, float64(1392065939000000000)) decoder.Shutdown() }) c.Specify("decodes an invalid messages", func() { data := "bogus message" pack.Message.SetPayload(data) packs, err := decoder.Decode(pack) c.Expect(len(packs), gs.Equals, 0) c.Expect(err.Error(), gs.Equals, "Failed parsing: "+data) c.Expect(decoder.processMessageFailures, gs.Equals, int64(1)) decoder.Shutdown() }) }) c.Specify("mysql decoder", func() { decoder := new(SandboxDecoder) decoder.SetPipelineConfig(pConfig) conf := decoder.ConfigStruct().(*sandbox.SandboxConfig) conf.ScriptFilename = "../lua/decoders/mysql_slow_query.lua" conf.ModuleDirectory = "../../../../../../modules" conf.MemoryLimit = 8e6 conf.Config = make(map[string]interface{}) conf.Config["truncate_sql"] = int64(5) supply := make(chan *pipeline.PipelinePack, 1) pack := pipeline.NewPipelinePack(supply) dRunner := pm.NewMockDecoderRunner(ctrl) dRunner.EXPECT().Name().Return("SandboxDecoder") err := decoder.Init(conf) c.Assume(err, gs.IsNil) decoder.SetDecoderRunner(dRunner) c.Specify("decode standard slow query log", func() { data := `# User@Host: syncrw[syncrw] @ [127.0.0.1] # Query_time: 2.964652 Lock_time: 0.000050 Rows_sent: 251 Rows_examined: 9773 use widget; SET last_insert_id=999,insert_id=1000,timestamp=1399500744; # administrator command: do something /* [queryName=FIND_ITEMS] */ SELECT * FROM widget WHERE id = 10;` pack.Message.SetPayload(data) _, err = decoder.Decode(pack) c.Assume(err, gs.IsNil) c.Expect(pack.Message.GetTimestamp(), gs.Equals, int64(1399500744000000000)) c.Expect(pack.Message.GetPayload(), gs.Equals, "/* [q...") c.Expect(pack.Message.GetType(), gs.Equals, "mysql.slow-query") decoder.Shutdown() }) }) c.Specify("mariadb decoder", func() { decoder := new(SandboxDecoder) decoder.SetPipelineConfig(pConfig) conf := decoder.ConfigStruct().(*sandbox.SandboxConfig) conf.ScriptFilename = "../lua/decoders/mariadb_slow_query.lua" conf.ModuleDirectory = "../../../../../../modules" conf.MemoryLimit = 8e6 conf.Config = make(map[string]interface{}) conf.Config["truncate_sql"] = int64(5) supply := make(chan *pipeline.PipelinePack, 1) pack := pipeline.NewPipelinePack(supply) dRunner := pm.NewMockDecoderRunner(ctrl) dRunner.EXPECT().Name().Return("SandboxDecoder") err := decoder.Init(conf) c.Assume(err, gs.IsNil) decoder.SetDecoderRunner(dRunner) c.Specify("decode standard slow query log", func() { data := `# User@Host: syncrw[syncrw] @ [127.0.0.1] # Thread_id: 110804 Schema: weave0 QC_hit: No # Query_time: 1.178108 Lock_time: 0.000053 Rows_sent: 198 Rows_examined: 198 SET timestamp=1399500744; /* [queryName=FIND_ITEMS] */ SELECT * FROM widget WHERE id = 10;` pack.Message.SetPayload(data) _, err = decoder.Decode(pack) c.Assume(err, gs.IsNil) c.Expect(pack.Message.GetTimestamp(), gs.Equals, int64(1399500744000000000)) c.Expect(pack.Message.GetPayload(), gs.Equals, "/* [q...") c.Expect(pack.Message.GetType(), gs.Equals, "mariadb.slow-query") decoder.Shutdown() }) }) }
func TestLocatorReadyNotify(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() uaid := "fce61180716a40ed8e79bf5ff0ba34bc" mckLogger := NewMockLogger(mockCtrl) mckLogger.EXPECT().ShouldLog(gomock.Any()).Return(true).AnyTimes() mckLogger.EXPECT().Log(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() var ( // routerPipes maps fake peer addresses to their respective pipes. Used // by dialRouter to connect to peers. routerPipes = make(map[netAddr]*pipeListener) // contacts is a list of peer URLs for the locator. contacts []string ) // Fake listener for the sender's router, used to test self-routing. sndRouterAddr := netAddr{"tcp", "snd-router.example.com:3000"} sndRouterPipe := newPipeListener() defer sndRouterPipe.Close() routerPipes[sndRouterAddr] = sndRouterPipe contacts = append(contacts, "http://snd-router.example.com:3000") // Fake listener for the receiver's router, used to test routing updates // to different hosts. recvRouterAddr := netAddr{"tcp", "recv-router.example.com:3000"} recvRouterPipe := newPipeListener() defer recvRouterPipe.Close() routerPipes[recvRouterAddr] = recvRouterPipe contacts = append(contacts, "http://recv-router.example.com:3000") // Fake listener for the receiver's WebSocket handler, used to accept a // WebSocket client connection. socketHandlerPipe := newPipeListener() defer socketHandlerPipe.Close() // Fake locator. mckLocator := NewMockLocator(mockCtrl) mckLocator.EXPECT().Contacts(uaid).Return(contacts, nil).Times(2) // Fake dialer to connect to each peer's routing listener. dialRouter := func(network, address string) (net.Conn, error) { if pipe, ok := routerPipes[netAddr{network, address}]; ok { return pipe.Dial(network, address) } return nil, &netErr{temporary: false, timeout: false} } // Configures a fake router for the app. setRouter := func(app *Application, listener net.Listener) { r := NewBroadcastRouter() r.setApp(app) r.setClientOptions(10, 3*time.Second, 3*time.Second) // Defaults. r.setClientTransport(&http.Transport{Dial: dialRouter}) r.listenWithConfig(listenerConfig{listener: listener}) r.maxDataLen = 4096 r.server = newServeWaiter(&http.Server{Handler: r.ServeMux()}) app.SetRouter(r) } // sndApp is the server broadcasting the update. The locator returns the // addresses of the sender and receiver to test self-routing. sndApp := NewApplication() sndApp.SetLogger(mckLogger) sndStat := NewMockStatistician(mockCtrl) sndApp.SetMetrics(sndStat) sndStore := NewMockStore(mockCtrl) sndApp.SetStore(sndStore) sndApp.SetLocator(mckLocator) // Set up a fake router for the sender. setRouter(sndApp, sndRouterPipe) // recvApp is the server receiving the update. recvApp := NewApplication() recvApp.SetLogger(mckLogger) recvStat := NewMockStatistician(mockCtrl) recvApp.SetMetrics(recvStat) recvStore := NewMockStore(mockCtrl) recvApp.SetStore(recvStore) // Wrap the fake locator in a type that implements ReadyNotifier. recvLocator := newMockReadyNotifier(mckLocator) recvApp.SetLocator(recvLocator) // Set up a fake WebSocket handler for the receiver. recvSocketHandler := NewSocketHandler() recvSocketHandler.setApp(recvApp) recvSocketHandler.listenWithConfig(listenerConfig{ listener: socketHandlerPipe}) recvSocketHandler.server = newServeWaiter(&http.Server{Handler: recvSocketHandler.ServeMux()}) recvApp.SetSocketHandler(recvSocketHandler) // Set up a fake router for the receiver. setRouter(recvApp, recvRouterPipe) chid := "2b7c5c27d6224bfeaf1c158c3c57fca3" version := int64(2) data := "I'm a little teapot, short and stout." var wg sync.WaitGroup // Waits for the client to close. wg.Add(1) dialChan := make(chan bool) // Signals when the client connects. timeout := closeAfter(2 * time.Second) go func() { defer wg.Done() origin := &url.URL{Scheme: "ws", Host: "recv-conn.example.com"} ws, err := dialSocketListener(socketHandlerPipe, &websocket.Config{ Location: origin, Origin: origin, Version: websocket.ProtocolVersionHybi13, }) if err != nil { t.Errorf("Error dialing host: %s", err) return } defer ws.Close() err = websocket.JSON.Send(ws, struct { Type string `json:"messageType"` DeviceID string `json:"uaid"` ChannelIDs []string `json:"channelIDs"` }{"hello", uaid, []string{}}) if err != nil { t.Errorf("Error writing handshake request: %s", err) return } helloReply := new(HelloReply) if err = websocket.JSON.Receive(ws, helloReply); err != nil { t.Errorf("Error reading handshake reply: %s", err) return } select { case dialChan <- true: case <-timeout: t.Errorf("Timed out waiting for router") return } flushReply := new(FlushReply) if err = websocket.JSON.Receive(ws, flushReply); err != nil { t.Errorf("Error reading routed update: %s", err) return } ok := false expected := Update{chid, uint64(version), data} for _, update := range flushReply.Updates { if ok = update == expected; ok { break } } if !ok { t.Errorf("Missing update %#v in %#v", expected, flushReply.Updates) return } }() // Start the handlers. errChan := make(chan error, 3) go sndApp.Router().Start(errChan) go recvApp.SocketHandler().Start(errChan) go recvApp.Router().Start(errChan) // First and second routing attempts to self. sndStat.EXPECT().Increment("updates.routed.unknown").Times(2) // Initial routing attempt to peer. recvStat.EXPECT().Increment("updates.routed.unknown") // Client connects to peer. recvStat.EXPECT().Increment("client.socket.connect") recvStore.EXPECT().CanStore(0).Return(true) recvStat.EXPECT().Increment("updates.client.hello") recvStore.EXPECT().FetchAll(uaid, gomock.Any()).Return(nil, nil, nil) recvStat.EXPECT().Timer("client.flush", gomock.Any()) // Second routing attempt to peer. recvStat.EXPECT().Increment("updates.routed.incoming") recvStat.EXPECT().Increment("updates.sent") recvStat.EXPECT().Timer("client.flush", gomock.Any()) recvStat.EXPECT().Increment("updates.routed.received") recvStat.EXPECT().Timer("client.socket.lifespan", gomock.Any()) recvStat.EXPECT().Increment("client.socket.disconnect") // Initial routing attempt should fail; the WebSocket listener shouldn't // accept client connections before the locator is ready. delivered, err := sndApp.Router().Route(nil, uaid, chid, version, timeNow(), "disconnected", data) if err != nil { t.Errorf("Error routing to disconnected client: %s", err) } else if delivered { t.Error("Should not route to disconnected client") } // Signal the locator is ready, then wait for the client to connect. recvLocator.SignalReady() select { case <-dialChan: case <-time.After(5 * time.Second): t.Fatalf("Timed out waiting for the client to connect") } // Routing should succeed once the client is connected. delivered, err = sndApp.Router().Route(nil, uaid, chid, version, timeNow(), "connected", data) if err != nil { t.Errorf("Error routing to connected client: %s", err) } else if !delivered { t.Error("Should route to connected client") } gomock.InOrder( mckLocator.EXPECT().Close(), recvStore.EXPECT().Close(), ) if err := recvApp.Close(); err != nil { t.Errorf("Error closing peer: %s", err) } wg.Wait() gomock.InOrder( mckLocator.EXPECT().Close(), sndStore.EXPECT().Close(), ) if err := sndApp.Close(); err != nil { t.Errorf("Error closing self: %s", err) } // Wait for the handlers to stop. for i := 0; i < 3; i++ { <-errChan } }
func TestEndpointResolveKey(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() mckLogger := NewMockLogger(mockCtrl) mckLogger.EXPECT().ShouldLog(gomock.Any()).Return(true).AnyTimes() mckLogger.EXPECT().Log(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() mckStat := NewMockStatistician(mockCtrl) mckStore := NewMockStore(mockCtrl) Convey("Endpoint tokens", t, func() { app := NewApplication() app.SetLogger(mckLogger) app.SetMetrics(mckStat) app.SetStore(mckStore) Convey("Should return a 404 for invalid tokens", func() { app.SetTokenKey("c3v0AlmmxXu_LSfdZY3l3eayLsIwkX48") eh := NewEndpointHandler() eh.setApp(app) app.SetEndpointHandler(eh) resp := httptest.NewRecorder() req := &http.Request{ Method: "PUT", Header: http.Header{}, URL: &url.URL{ Path: "/update/j1bqzFq9WiwFZbqay-y7xVlfSvtO1eY="}, // "123.456" } gomock.InOrder( mckStore.EXPECT().KeyToIDs("123.456").Return("", "", ErrInvalidKey), mckStat.EXPECT().Increment("updates.appserver.invalid"), ) eh.ServeMux().ServeHTTP(resp, req) So(resp.Code, ShouldEqual, 404) body, isJSON := getJSON(resp.HeaderMap, resp.Body) So(isJSON, ShouldBeTrue) So(body.String(), ShouldEqual, `"Invalid Token"`) }) Convey("Should not decode plaintext tokens without a key", func() { var err error app.SetTokenKey("") eh := NewEndpointHandler() eh.setApp(app) app.SetEndpointHandler(eh) _, err = eh.decodePK("") So(err, ShouldNotBeNil) pk, err := eh.decodePK("123.456") So(pk, ShouldEqual, "123.456") }) Convey("Should normalize decoded tokens", func() { app.SetTokenKey("LM1xDImCx0rB46LCnx-3v4-Iyfk1LeKJbx9wuvx_z3U=") eh := NewEndpointHandler() eh.setApp(app) app.SetEndpointHandler(eh) // Hyphenated IDs should be normalized. uaid := "dbda2ba2-004c-491f-9e3d-c5950aee93de" chid := "848cd568-3f2a-4108-9ce4-bd0d928ecad4" // " \t%s.%s\r\n" % (uaid, chid) encodedKey := "qfGSdZzwf20GXiYubmZfIXj11Rx4RGJujFsjSQGdF4LRBhHbB_vt3hdW7cRvL9Fq_t_guMBGkDgebOoa5gRd1GGLN-Cv6h5hkpRTbdju8Tk-hMyC91BP4CEres_8" // decodePK should trim whitespace from encoded keys. mckStore.EXPECT().KeyToIDs( fmt.Sprintf("%s.%s", uaid, chid)).Return(uaid, chid, nil) actualUAID, actualCHID, err := eh.resolvePK(encodedKey) So(err, ShouldBeNil) So(actualUAID, ShouldEqual, uaid) So(actualCHID, ShouldEqual, chid) }) Convey("Should reject invalid tokens", func() { var err error app.SetTokenKey("IhnNwMNbsFWiafTXSgF4Ag==") eh := NewEndpointHandler() eh.setApp(app) app.SetEndpointHandler(eh) invalidKey := "b54QOw2omSWBiEq0IuyfBGxHBIR7AI9YhCMA0lP9" // "_=!@#$%^&*()[]" uaid := "82398a648c834f8b838cb3945eceaf29" chid := "af445ad07e5f46b7a6c858150fc5aa92" validKey := fmt.Sprintf("%s.%s", uaid, chid) encodedKey := "swKSH8P2qprRt5y0J4Wi7ybl-qzFv1j09WPOfuabpEJmVUqwUpxjprXc2R3Yw0ITbqc_Swntw9_EpCgo_XuRTn7Q7opQYoQUgMPhCgT0EGbK" _, _, err = eh.resolvePK(invalidKey[:8]) So(err, ShouldNotBeNil) _, _, err = eh.resolvePK(invalidKey) So(err, ShouldNotBeNil) // Reject plaintext tokens if a key is specified. _, _, err = eh.resolvePK(validKey) So(err, ShouldNotBeNil) mckStore.EXPECT().KeyToIDs(validKey).Return("", "", ErrInvalidKey) _, _, err = eh.resolvePK(encodedKey) So(err, ShouldNotBeNil) mckStore.EXPECT().KeyToIDs(validKey).Return(uaid, chid, nil) actualUAID, actualCHID, err := eh.resolvePK(encodedKey) So(err, ShouldBeNil) So(actualUAID, ShouldEqual, uaid) So(actualCHID, ShouldEqual, chid) }) }) }
func TestEndpointDelivery(t *testing.T) { useMockFuncs() defer useStdFuncs() mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() mckLogger := NewMockLogger(mockCtrl) mckLogger.EXPECT().ShouldLog(gomock.Any()).Return(true).AnyTimes() mckLogger.EXPECT().Log(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() mckStat := NewMockStatistician(mockCtrl) mckStore := NewMockStore(mockCtrl) mckRouter := NewMockRouter(mockCtrl) mckWorker := NewMockWorker(mockCtrl) Convey("Update delivery", t, func() { app := NewApplication() app.SetLogger(mckLogger) app.SetMetrics(mckStat) app.SetStore(mckStore) app.SetRouter(mckRouter) Convey("Should attempt local delivery if `AlwaysRoute` is disabled", func() { eh := NewEndpointHandler() eh.setApp(app) app.SetEndpointHandler(eh) Convey("Should route updates if the device is not connected", func() { uaid := "f7e9fc483f7344c398701b6fa0e85e4f" chid := "737b7a0d25674be4bb184f015fce02cf" gomock.InOrder( mckStat.EXPECT().Increment("updates.routed.outgoing"), mckRouter.EXPECT().Route(nil, uaid, chid, int64(3), timeNow().UTC(), "", "").Return(true, nil), mckStat.EXPECT().Increment("router.broadcast.hit"), mckStat.EXPECT().Timer("updates.routed.hits", gomock.Any()), mckStat.EXPECT().Increment("updates.appserver.received"), ) ok := eh.deliver(nil, uaid, chid, 3, "", "") So(ok, ShouldBeTrue) }) // A routing failure still stores the alert for potential // client delivery. We should only return 404 for absolute // failures (where the endpoint is no longer valid Convey("Should return a 202 if routing fails", func() { resp := httptest.NewRecorder() req := &http.Request{ Method: "PUT", Header: http.Header{HeaderID: {"reqID"}}, URL: &url.URL{Path: "/update/123"}, Body: formReader(url.Values{"version": {"1"}}), } gomock.InOrder( mckStore.EXPECT().KeyToIDs("123").Return("123", "456", nil), mckStat.EXPECT().Increment("updates.appserver.incoming"), mckStore.EXPECT().Update("123", "456", int64(1)).Return(nil), mckStat.EXPECT().Increment("updates.routed.outgoing"), mckRouter.EXPECT().Route(nil, "123", "456", int64(1), gomock.Any(), "reqID", "").Return(false, nil), mckStat.EXPECT().Increment("router.broadcast.miss"), mckStat.EXPECT().Timer("updates.routed.misses", gomock.Any()), mckStat.EXPECT().Increment("updates.appserver.rejected"), ) eh.ServeMux().ServeHTTP(resp, req) So(resp.Code, ShouldEqual, 202) body, isJSON := getJSON(resp.HeaderMap, resp.Body) So(isJSON, ShouldBeTrue) So(body.String(), ShouldEqual, "{}") }) Convey("Should return a 404 if local delivery fails", func() { uaid := "9e98d6415d8e4fd099ab1bad7178f750" chid := "0eecf572e99f4d508666d8da6c0b15a9" app.AddWorker(uaid, mckWorker) gomock.InOrder( mckWorker.EXPECT().Send(chid, int64(3), "").Return( errors.New("client gone")), mckStat.EXPECT().Increment("updates.appserver.rejected"), ) ok := eh.deliver(nil, uaid, chid, int64(3), "", "") So(ok, ShouldBeFalse) }) Convey("Should return an error if storage is unavailable", func() { resp := httptest.NewRecorder() req := &http.Request{ Method: "PUT", Header: http.Header{}, URL: &url.URL{Path: "/update/123"}, Body: formReader(url.Values{"version": {"2"}}), } updateErr := ErrInvalidChannel gomock.InOrder( mckStore.EXPECT().KeyToIDs("123").Return("123", "456", nil), mckStat.EXPECT().Increment("updates.appserver.incoming"), mckStore.EXPECT().Update("123", "456", int64(2)).Return(updateErr), mckStat.EXPECT().Increment("updates.appserver.error"), ) eh.ServeMux().ServeHTTP(resp, req) So(resp.Code, ShouldEqual, updateErr.Status()) body, isJSON := getJSON(resp.HeaderMap, resp.Body) So(isJSON, ShouldBeTrue) So(body.String(), ShouldEqual, `"Could not update channel version"`) }) }) Convey("Should always route updates if `AlwaysRoute` is enabled", func() { eh := NewEndpointHandler() eh.setApp(app) eh.alwaysRoute = true app.SetEndpointHandler(eh) uaid := "6952a68ee0e7444ebc54f935c4444b13" app.AddWorker(uaid, mckWorker) chid := "b7ede546585f4cc9b95e9340e3406951" version := int64(1) data := "Happy, happy, joy, joy!" Convey("And router delivery fails, local succeeds", func() { gomock.InOrder( mckStat.EXPECT().Increment("updates.routed.outgoing"), mckRouter.EXPECT().Route(nil, uaid, chid, version, gomock.Any(), "", data).Return(false, nil), mckStat.EXPECT().Increment("router.broadcast.miss"), mckStat.EXPECT().Timer("updates.routed.misses", gomock.Any()), mckWorker.EXPECT().Send(chid, version, data).Return(nil), mckStat.EXPECT().Increment("updates.appserver.received"), ) ok := eh.deliver(nil, uaid, chid, version, "", data) So(ok, ShouldBeTrue) }) Convey("And router delivery succeeds, local succeeds", func() { gomock.InOrder( mckStat.EXPECT().Increment("updates.routed.outgoing"), mckRouter.EXPECT().Route(nil, uaid, chid, version, gomock.Any(), "", data).Return(true, nil), mckStat.EXPECT().Increment("router.broadcast.hit"), mckStat.EXPECT().Timer("updates.routed.hits", gomock.Any()), mckWorker.EXPECT().Send(chid, version, data).Return(nil), mckStat.EXPECT().Increment("updates.appserver.received"), ) ok := eh.deliver(nil, uaid, chid, version, "", data) So(ok, ShouldBeTrue) }) Convey("And router delivery succeeds, local fails", func() { gomock.InOrder( mckStat.EXPECT().Increment("updates.routed.outgoing"), mckRouter.EXPECT().Route(nil, uaid, chid, version, gomock.Any(), "", data).Return(true, nil), mckStat.EXPECT().Increment("router.broadcast.hit"), mckStat.EXPECT().Timer("updates.routed.hits", gomock.Any()), mckWorker.EXPECT().Send(chid, version, data).Return( errors.New("client gone")), mckStat.EXPECT().Increment("updates.appserver.received"), ) ok := eh.deliver(nil, uaid, chid, version, "", data) So(ok, ShouldBeTrue) }) Convey("And router/local delivery fails", func() { gomock.InOrder( mckStat.EXPECT().Increment("updates.routed.outgoing"), mckRouter.EXPECT().Route(nil, uaid, chid, version, gomock.Any(), "", data).Return(false, nil), mckStat.EXPECT().Increment("router.broadcast.miss"), mckStat.EXPECT().Timer("updates.routed.misses", gomock.Any()), mckWorker.EXPECT().Send(chid, version, data).Return( errors.New("client gone")), mckStat.EXPECT().Increment("updates.appserver.rejected"), ) ok := eh.deliver(nil, uaid, chid, version, "", data) So(ok, ShouldBeFalse) }) }) }) }
func TestEndpointPinger(t *testing.T) { useMockFuncs() defer useStdFuncs() mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() mckLogger := NewMockLogger(mockCtrl) mckLogger.EXPECT().ShouldLog(gomock.Any()).Return(true).AnyTimes() mckLogger.EXPECT().Log(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() mckStat := NewMockStatistician(mockCtrl) mckPinger := NewMockPropPinger(mockCtrl) mckStore := NewMockStore(mockCtrl) mckWorker := NewMockWorker(mockCtrl) Convey("Proprietary pings", t, func() { app := NewApplication() app.SetLogger(mckLogger) app.SetMetrics(mckStat) app.SetPropPinger(mckPinger) app.SetStore(mckStore) eh := NewEndpointHandler() eh.setApp(app) eh.setMaxDataLen(4096) app.SetEndpointHandler(eh) Convey("Should return early if the pinger can bypass the WebSocket", func() { uaid := "91357e1a34714cadacb3f13cf47a2736" app.AddWorker(uaid, mckWorker) resp := httptest.NewRecorder() req := &http.Request{ Method: "PUT", Header: http.Header{}, URL: &url.URL{Path: "/update/123"}, Body: nil, } gomock.InOrder( mckStore.EXPECT().KeyToIDs("123").Return(uaid, "456", nil), mckStat.EXPECT().Increment("updates.appserver.incoming"), mckPinger.EXPECT().Send(uaid, int64(1257894000), "").Return(true, nil), mckPinger.EXPECT().CanBypassWebsocket().Return(true), mckStat.EXPECT().Increment("updates.appserver.received"), mckStat.EXPECT().Timer("updates.handled", gomock.Any()), ) eh.ServeMux().ServeHTTP(resp, req) So(resp.Code, ShouldEqual, 200) body, isJSON := getJSON(resp.HeaderMap, resp.Body) So(isJSON, ShouldBeTrue) So(body.String(), ShouldEqual, "{}") }) Convey("Should continue if the pinger cannot bypass the WebSocket", func() { uaid := "e3fc2cf1dc44424685010148b076d08b" app.AddWorker(uaid, mckWorker) data := randomText(eh.maxDataLen) vals := make(url.Values) vals.Set("data", data) resp := httptest.NewRecorder() req := &http.Request{ Method: "PUT", Header: http.Header{}, URL: &url.URL{Path: "/update/123"}, Body: formReader(vals), } gomock.InOrder( mckStore.EXPECT().KeyToIDs("123").Return(uaid, "456", nil), mckStat.EXPECT().Increment("updates.appserver.incoming"), mckPinger.EXPECT().Send(uaid, int64(1257894000), data).Return(true, nil), mckPinger.EXPECT().CanBypassWebsocket().Return(false), mckStore.EXPECT().Update(uaid, "456", int64(1257894000)), mckWorker.EXPECT().Send("456", int64(1257894000), data), mckStat.EXPECT().Increment("updates.appserver.received"), mckStat.EXPECT().Timer("updates.handled", gomock.Any()), ) eh.ServeMux().ServeHTTP(resp, req) So(resp.Code, ShouldEqual, 200) body, isJSON := getJSON(resp.HeaderMap, resp.Body) So(isJSON, ShouldBeTrue) So(body.String(), ShouldEqual, "{}") }) Convey("Should continue if the pinger fails", func() { uaid := "8f412f5cb2384183bf60f7da26737271" app.AddWorker(uaid, mckWorker) vals := make(url.Values) vals.Set("version", "7") resp := httptest.NewRecorder() req := &http.Request{ Method: "PUT", Header: http.Header{}, URL: &url.URL{Path: "/update/123"}, Body: formReader(vals), } gomock.InOrder( mckStore.EXPECT().KeyToIDs("123").Return(uaid, "456", nil), mckStat.EXPECT().Increment("updates.appserver.incoming"), mckPinger.EXPECT().Send(uaid, int64(7), "").Return( true, errors.New("oops")), mckStore.EXPECT().Update(uaid, "456", int64(7)), mckWorker.EXPECT().Send("456", int64(7), ""), mckStat.EXPECT().Increment("updates.appserver.received"), mckStat.EXPECT().Timer("updates.handled", gomock.Any()), ) eh.ServeMux().ServeHTTP(resp, req) So(resp.Code, ShouldEqual, 200) body, isJSON := getJSON(resp.HeaderMap, resp.Body) So(isJSON, ShouldBeTrue) So(body.String(), ShouldEqual, "{}") }) }) }
func TestSocketOrigin(t *testing.T) { var err error mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() mckLogger := NewMockLogger(mockCtrl) mckLogger.EXPECT().ShouldLog(gomock.Any()).Return(true).AnyTimes() mckLogger.EXPECT().Log(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() mckStat := NewMockStatistician(mockCtrl) mckStore := NewMockStore(mockCtrl) mckRouter := NewMockRouter(mockCtrl) app := NewApplication() app.SetLogger(mckLogger) app.SetMetrics(mckStat) app.SetStore(mckStore) app.SetRouter(mckRouter) sh := NewSocketHandler() defer sh.Close() sh.setApp(app) pipe := newPipeListener() defer pipe.Close() if err := sh.listenWithConfig(listenerConfig{listener: pipe}); err != nil { t.Fatalf("Error setting listener: %s", err) } sh.server = newServeWaiter(&http.Server{Handler: sh.ServeMux()}) app.SetSocketHandler(sh) errChan := make(chan error, 1) go sh.Start(errChan) uaid := "5e1e5984569c4f00bf4bea47754a6403" gomock.InOrder( mckStat.EXPECT().Increment("client.socket.connect"), mckStore.EXPECT().CanStore(0).Return(true), mckRouter.EXPECT().Register(uaid), mckStat.EXPECT().Increment("updates.client.hello"), mckStore.EXPECT().FetchAll(uaid, gomock.Any()).Return(nil, nil, nil), mckStat.EXPECT().Timer("client.flush", gomock.Any()), mckRouter.EXPECT().Unregister(uaid), mckStat.EXPECT().Timer("client.socket.lifespan", gomock.Any()), mckStat.EXPECT().Increment("client.socket.disconnect"), ) origin := &url.URL{Scheme: "https", Host: "example.com"} conn, err := dialSocketListener(pipe, &websocket.Config{ Location: origin, Origin: origin, Version: websocket.ProtocolVersionHybi13, }) if err != nil { t.Fatalf("Error dialing origin: %s", err) } defer conn.Close() err = websocket.JSON.Send(conn, struct { Type string `json:"messageType"` DeviceID string `json:"uaid"` ChannelIDs []string `json:"channelIDs"` }{"hello", uaid, []string{}}) if err != nil { t.Fatalf("Error writing client handshake: %s", err) } reply := new(HelloReply) if err = websocket.JSON.Receive(conn, reply); err != nil { t.Fatalf("Error reading server handshake: %s", err) } if reply.DeviceID != uaid { t.Fatalf("Mismatched device ID: got %q; want %q", reply.DeviceID, uaid) } worker, workerConnected := app.GetWorker(uaid) if !workerConnected { t.Fatalf("Missing worker for device ID %q", uaid) } workerOrigin := worker.Origin() if expectedOrigin := origin.String(); workerOrigin != expectedOrigin { t.Errorf("Mismatched origins: got %q; want %q", workerOrigin, expectedOrigin) } }
func IrcOutputSpec(c gs.Context) { t := new(pipeline_ts.SimpleT) ctrl := gomock.NewController(t) defer ctrl.Finish() pipelineConfig := NewPipelineConfig(nil) outTestHelper := plugins_ts.NewOutputTestHelper(ctrl) var wg sync.WaitGroup errChan := make(chan error, 1) tickChan := make(chan time.Time) inChan := make(chan *PipelinePack, 5) c.Specify("An IrcOutput", func() { ircOutput := new(IrcOutput) ircOutput.InitIrcCon = NewMockIrcConn encoder := new(plugins.PayloadEncoder) encoder.Init(&plugins.PayloadEncoderConfig{}) config := ircOutput.ConfigStruct().(*IrcOutputConfig) config.Server = "irc.example.org" config.Nick = "heka_bot" config.Ident = "heka" config.Channels = []string{"#test_channel"} msg := pipeline_ts.GetTestMessage() pack := NewPipelinePack(pipelineConfig.InputRecycleChan()) pack.Message = msg pack.Decoded = true c.Specify("requires an encoder", func() { err := ircOutput.Init(config) c.Assume(err, gs.IsNil) outTestHelper.MockOutputRunner.EXPECT().Encoder().Return(nil) err = ircOutput.Run(outTestHelper.MockOutputRunner, outTestHelper.MockHelper) c.Expect(err, gs.Not(gs.IsNil)) }) c.Specify("that is started", func() { outTestHelper.MockOutputRunner.EXPECT().Ticker().Return(tickChan) outTestHelper.MockOutputRunner.EXPECT().Encoder().Return(encoder) outTestHelper.MockOutputRunner.EXPECT().InChan().Return(inChan) outTestHelper.MockOutputRunner.EXPECT().Encode(pack).Return(encoder.Encode(pack)).AnyTimes() startOutput := func() { wg.Add(1) go func() { err := ircOutput.Run(outTestHelper.MockOutputRunner, outTestHelper.MockHelper) errChan <- err wg.Done() }() <-mockIrcConn.connected } c.Specify("sends incoming messages to an irc server", func() { err := ircOutput.Init(config) c.Assume(err, gs.IsNil) c.Assume(len(ircOutput.Channels), gs.Equals, 1) ircChan := ircOutput.Channels[0] startOutput() // Send the data inChan <- pack // wait for it to arrive p := <-ircOutput.OutQueue ircOutput.OutQueue <- p // once it's arrived send a tick so it gets processed tickChan <- time.Now() close(inChan) wg.Wait() // Verify we've exited c.Expect(mockIrcConn.quit, gs.IsTrue) // verify the message was sent msgs := mockIrcConn.msgs[ircChan] c.Expect(len(msgs), gs.Equals, 1) c.Expect(msgs[0], gs.Equals, string(*msg.Payload)) }) c.Specify("drops messages when outqueue is full", func() { config.QueueSize = 1 err := ircOutput.Init(config) c.Assume(err, gs.IsNil) c.Assume(len(ircOutput.Channels), gs.Equals, 1) ircChan := ircOutput.Channels[0] outTestHelper.MockOutputRunner.EXPECT().LogError( ErrOutQueueFull) outTestHelper.MockOutputRunner.EXPECT().LogError( fmt.Errorf("%s Channel: %s.", ErrBacklogQueueFull, ircChan)) startOutput() // Need to wait for callbacks to get registered before calling part. // If we've called connected, then callbacks have been registered. // Leave the irc channel so we can fill up the backlog ircOutput.Conn.Part(ircChan) c.Expect(ircOutput.JoinedChannels[0], gs.Equals, NOTJOINED) // Put some data into the inChan inChan <- pack // Wait for it to arrive in the OutQueue and put it back // We *must* do this before sending the tick to avoid data races p := <-ircOutput.OutQueue ircOutput.OutQueue <- p // One tick so that it goes from the OutQueue into the BacklogQueue tickChan <- time.Now() // Verify the item is in the backlog queue and put it back // (It will never leave this loop if the msg never arrives) queue := ircOutput.BacklogQueues[0] msg := <-queue queue <- msg c.Expect(len(queue), gs.Equals, 1) // Send another message to fill the OutQueue inChan <- pack // Wait for it to arrive, again p = <-ircOutput.OutQueue ircOutput.OutQueue <- p // Send the tick after it's in the OutQueue so we can try processing // it. This is where we drop it since we cant send, and the // BacklogQueue is already full. tickChan <- time.Now() // Now we want to also cause the OutQueue to drop a message. // We don't have to wait for it to arrive in the OutQueue like we do above // since it shouldn't make it there. inChan <- pack inChan <- pack close(inChan) wg.Wait() // Verify we've exited c.Expect(mockIrcConn.quit, gs.IsTrue) // verify the backlog queue is empty c.Expect(len(queue), gs.Equals, 0) // verify that no messages were sent. msgs := mockIrcConn.msgs[ircChan] c.Expect(len(msgs), gs.Equals, 0) }) c.Specify("automatically reconnects on disconnect", func() { config.TimeBeforeReconnect = 0 err := ircOutput.Init(config) c.Assume(err, gs.IsNil) outTestHelper.MockOutputRunner.EXPECT().LogMessage(DisconnectMsg) outTestHelper.MockOutputRunner.EXPECT().LogMessage(ReconnectedMsg) startOutput() ircOutput.Conn.Disconnect() // verify we've been disconnected c.Expect(<-mockIrcConn.connected, gs.IsFalse) // Wait to become reconnected. c.Expect(<-mockIrcConn.connected, gs.IsTrue) close(inChan) wg.Wait() c.Expect(mockIrcConn.quit, gs.IsTrue) }) c.Specify("logs an error and exits from Run() when it cannot reconnect", func() { config.TimeBeforeReconnect = 0 err := ircOutput.Init(config) c.Assume(err, gs.IsNil) // Explicitly fail to reconnect mockIrcConn.failReconnect = true outTestHelper.MockOutputRunner.EXPECT().LogMessage(DisconnectMsg) outTestHelper.MockOutputRunner.EXPECT().LogError(fmt.Errorf(ErrReconnecting, mockErrFailReconnect)) startOutput() // Need to wait for callbacks to get registered before we disconnect ircOutput.Conn.Disconnect() // verify we've been disconnected // Run() should return without closing the inChan because the plugin // automatically cleans up and returns if it fails to reconnect wg.Wait() c.Expect(mockIrcConn.quit, gs.IsTrue) // do this at the end for test cleanup purposes close(inChan) }) c.Specify("when kicked from an irc channel", func() { c.Specify("rejoins when configured to do so", func() { config.RejoinOnKick = true err := ircOutput.Init(config) c.Assume(err, gs.IsNil) c.Assume(len(ircOutput.Channels), gs.Equals, 1) ircChan := ircOutput.Channels[0] startOutput() // trigger a kick event for the irc channel kick(mockIrcConn, ircChan) c.Expect(ircOutput.JoinedChannels[0], gs.Equals, JOINED) // quit close(inChan) }) c.Specify("doesnt rejoin when it isnt configured to", func() { config.RejoinOnKick = false // this is already the default err := ircOutput.Init(config) c.Assume(err, gs.IsNil) c.Assume(len(ircOutput.Channels), gs.Equals, 1) ircChan := ircOutput.Channels[0] startOutput() // Since this is the only channel we're in, we should get an // error that there are no channels to join left after being // kicked outTestHelper.MockOutputRunner.EXPECT().LogError(ErrNoJoinableChannels) // trigger a kick for the irc channel kick(mockIrcConn, ircChan) c.Expect(ircOutput.JoinedChannels[0], gs.Equals, CANNOTJOIN) // We shouldnt need to close the inChan, since we have no // joinable channels, we should be cleaning up already. }) wg.Wait() }) c.Specify("when trying to join an unjoinable channel", func() { c.Specify("it logs an error", func() { config.Channels = []string{"foo", "baz"} err := ircOutput.Init(config) c.Assume(err, gs.IsNil) c.Assume(len(ircOutput.Channels), gs.Equals, 2) ircChan := ircOutput.Channels[0] startOutput() c.Expect(ircOutput.JoinedChannels[0], gs.Equals, JOINED) reason := "foo" err = IrcCannotJoinError{ircChan, reason} outTestHelper.MockOutputRunner.EXPECT().LogError(err) args := []string{"", ircChan, reason} event := irc.Event{Arguments: args} c.Specify("when the channel key is wrong", func() { event.Code = IRC_ERR_BADCHANNELKEY }) c.Specify("when banned from the channel", func() { event.Code = IRC_ERR_BANNEDFROMCHAN }) c.Specify("when there is no such channel", func() { event.Code = IRC_NOSUCHCHANNEL }) c.Specify("when the channel is invite only", func() { event.Code = IRC_ERR_INVITEONLYCHAN }) ircOutput.Conn.RunCallbacks(&event) c.Expect(ircOutput.JoinedChannels[0], gs.Equals, CANNOTJOIN) // should still be unable to join ircOutput.Join(ircChan) c.Expect(ircOutput.JoinedChannels[0], gs.Equals, CANNOTJOIN) close(inChan) }) c.Specify("it exits if there are no joinable channels", func() { err := ircOutput.Init(config) c.Assume(err, gs.IsNil) c.Assume(len(ircOutput.Channels), gs.Equals, 1) ircChan := ircOutput.Channels[0] startOutput() c.Expect(ircOutput.JoinedChannels[0], gs.Equals, JOINED) reason := "foo" err = IrcCannotJoinError{ircChan, reason} outTestHelper.MockOutputRunner.EXPECT().LogError(err) outTestHelper.MockOutputRunner.EXPECT().LogError(ErrNoJoinableChannels) args := []string{"", ircChan, reason} event := irc.Event{Arguments: args, Code: IRC_ERR_BADCHANNELKEY} ircOutput.Conn.RunCallbacks(&event) // No close since we should exit without. }) wg.Wait() }) c.Specify("when trying to join a full channel", func() { config.TimeBeforeRejoin = 0 err := ircOutput.Init(config) c.Assume(err, gs.IsNil) c.Assume(len(ircOutput.Channels), gs.Equals, 1) ircChan := ircOutput.Channels[0] startOutput() c.Expect(ircOutput.JoinedChannels[0], gs.Equals, JOINED) // We should be able to join before this ircOutput.Join(ircChan) reason := "full channel" err = fmt.Errorf("%s. Retrying in %d seconds.", IrcCannotJoinError{ircChan, reason}.Error(), ircOutput.TimeBeforeRejoin) args := []string{"", ircChan, reason} event := irc.Event{Code: IRC_ERR_CHANNELISFULL, Arguments: args} c.Specify("logs an error after using up its attempts", func() { mockIrcConn.failJoin = true // We should leave the channel so that we aren't just // causing a failure to join even when we're already in the // channel ircOutput.Conn.Part(ircChan) maxRetries := int(ircOutput.MaxJoinRetries) c.Expect(ircOutput.JoinedChannels[0], gs.Equals, NOTJOINED) gomock.InOrder( outTestHelper.MockOutputRunner.EXPECT().LogError( err).Times(maxRetries), outTestHelper.MockOutputRunner.EXPECT().LogError( fmt.Errorf(ErrUsedUpRetryAttempts, ircChan)), outTestHelper.MockOutputRunner.EXPECT().LogError( ErrNoJoinableChannels), ) for i := 1; i <= maxRetries; i++ { // Trigger the event the number of times ircOutput.Conn.RunCallbacks(&event) c.Expect(int(ircOutput.numRetries[ircChan]), gs.Equals, i) } c.Expect(ircOutput.JoinedChannels[0], gs.Equals, NOTJOINED) ircOutput.Conn.RunCallbacks(&event) // We've used up our attempts, c.Expect(ircOutput.JoinedChannels[0], gs.Equals, CANNOTJOIN) ircOutput.Conn.RunCallbacks(&event) c.Expect(ircOutput.JoinedChannels[0], gs.Equals, CANNOTJOIN) }) c.Specify("resets its attempts when it successful", func() { mockIrcConn.failJoin = true outTestHelper.MockOutputRunner.EXPECT().LogError(err).Times(2) ircOutput.Conn.RunCallbacks(&event) c.Expect(int(ircOutput.numRetries[ircChan]), gs.Equals, 1) mockIrcConn.failJoin = false ircOutput.Conn.RunCallbacks(&event) c.Expect(int(ircOutput.numRetries[ircChan]), gs.Equals, 0) }) close(inChan) wg.Wait() }) c.Specify("when banned from a server logs an error and quits", func() { err := ircOutput.Init(config) c.Assume(err, gs.IsNil) startOutput() event := irc.Event{Code: IRC_ERR_YOUREBANNEDCREEP} outTestHelper.MockOutputRunner.EXPECT().LogError(ErrBannedFromServer) ircOutput.Conn.RunCallbacks(&event) // We should be able to exit without closing the channel first wg.Wait() // Clean this up close(inChan) }) c.Expect(<-errChan, gs.IsNil) c.Expect(<-mockIrcConn.connected, gs.IsFalse) }) // Cleanup which should happen each run close(mockIrcConn.connected) close(mockIrcConn.delivered) close(mockIrcConn.Error) }) }