package goreplay import ( "bytes" "errors" "fmt" "io/ioutil" "math/rand" "os" "sync" "testing" "time" ) func TestInputFileWithGET(t *testing.T) { input := NewTestInput() rg := NewRequestGenerator([]PluginReader{input}, func() { input.EmitGET() }, 1) readPayloads := []*Message{} // Given a capture file with a GET request expectedCaptureFile := CreateCaptureFile(rg) defer expectedCaptureFile.TearDown() // When the request is read from the capture file err := ReadFromCaptureFile(expectedCaptureFile.file, 1, func(msg *Message) { readPayloads = append(readPayloads, msg) }) // The read request should match the original request if err != nil { t.Error(err) } else if !expectedCaptureFile.PayloadsEqual(readPayloads) { t.Error("Request read back from file should match") } } func TestInputFileWithPayloadLargerThan64Kb(t *testing.T) { input := NewTestInput() rg := NewRequestGenerator([]PluginReader{input}, func() { input.EmitSizedPOST(64 * 1024) }, 1) readPayloads := []*Message{} // Given a capture file with a request over 64Kb expectedCaptureFile := CreateCaptureFile(rg) defer expectedCaptureFile.TearDown() // When the request is read from the capture file err := ReadFromCaptureFile(expectedCaptureFile.file, 1, func(msg *Message) { readPayloads = append(readPayloads, msg) }) // The read request should match the original request if err != nil { t.Error(err) } else if !expectedCaptureFile.PayloadsEqual(readPayloads) { t.Error("Request read back from file should match") } } func TestInputFileWithGETAndPOST(t *testing.T) { input := NewTestInput() rg := NewRequestGenerator([]PluginReader{input}, func() { input.EmitGET() input.EmitPOST() }, 2) readPayloads := []*Message{} // Given a capture file with a GET request expectedCaptureFile := CreateCaptureFile(rg) defer expectedCaptureFile.TearDown() // When the requests are read from the capture file err := ReadFromCaptureFile(expectedCaptureFile.file, 2, func(msg *Message) { readPayloads = append(readPayloads, msg) }) // The read requests should match the original request if err != nil { t.Error(err) } else if !expectedCaptureFile.PayloadsEqual(readPayloads) { t.Error("Request read back from file should match") } } func TestInputFileMultipleFilesWithRequestsOnly(t *testing.T) { rnd := rand.Int63() file1, _ := os.OpenFile(fmt.Sprintf("/tmp/%d_0", rnd), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0660) file1.Write([]byte("1 1 1\ntest1")) file1.Write([]byte(payloadSeparator)) file1.Write([]byte("1 1 3\ntest2")) file1.Write([]byte(payloadSeparator)) file1.Close() file2, _ := os.OpenFile(fmt.Sprintf("/tmp/%d_1", rnd), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0660) file2.Write([]byte("1 1 2\ntest3")) file2.Write([]byte(payloadSeparator)) file2.Write([]byte("1 1 4\ntest4")) file2.Write([]byte(payloadSeparator)) file2.Close() input := NewFileInput(fmt.Sprintf("/tmp/%d*", rnd), false, 100, 0, false) for i := '1'; i <= '4'; i++ { msg, _ := input.PluginRead() if msg.Meta[4] != byte(i) { t.Error("Should emit requests in right order", string(msg.Meta)) } } os.Remove(file1.Name()) os.Remove(file2.Name()) } func TestInputFileRequestsWithLatency(t *testing.T) { rnd := rand.Int63() file, _ := os.OpenFile(fmt.Sprintf("/tmp/%d", rnd), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0660) defer file.Close() file.Write([]byte("1 1 100000000\nrequest1")) file.Write([]byte(payloadSeparator)) file.Write([]byte("1 2 150000000\nrequest2")) file.Write([]byte(payloadSeparator)) file.Write([]byte("1 3 250000000\nrequest3")) file.Write([]byte(payloadSeparator)) input := NewFileInput(fmt.Sprintf("/tmp/%d", rnd), false, 100, 0, false) start := time.Now().UnixNano() for i := 0; i < 3; i++ { input.PluginRead() } end := time.Now().UnixNano() var expectedLatency int64 = 300000000 - 100000000 realLatency := end - start if realLatency > expectedLatency { t.Errorf("Should emit requests respecting latency. Expected: %v, real: %v", expectedLatency, realLatency) } } func TestInputFileMultipleFilesWithRequestsAndResponses(t *testing.T) { rnd := rand.Int63() file1, _ := os.OpenFile(fmt.Sprintf("/tmp/%d_0", rnd), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0660) file1.Write([]byte("1 1 1\nrequest1")) file1.Write([]byte(payloadSeparator)) file1.Write([]byte("2 1 1\nresponse1")) file1.Write([]byte(payloadSeparator)) file1.Write([]byte("1 2 3\nrequest2")) file1.Write([]byte(payloadSeparator)) file1.Write([]byte("2 2 3\nresponse2")) file1.Write([]byte(payloadSeparator)) file1.Close() file2, _ := os.OpenFile(fmt.Sprintf("/tmp/%d_1", rnd), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0660) file2.Write([]byte("1 3 2\nrequest3")) file2.Write([]byte(payloadSeparator)) file2.Write([]byte("2 3 2\nresponse3")) file2.Write([]byte(payloadSeparator)) file2.Write([]byte("1 4 4\nrequest4")) file2.Write([]byte(payloadSeparator)) file2.Write([]byte("2 4 4\nresponse4")) file2.Write([]byte(payloadSeparator)) file2.Close() input := NewFileInput(fmt.Sprintf("/tmp/%d*", rnd), false, 100, 0, false) for i := '1'; i <= '4'; i++ { msg, _ := input.PluginRead() if msg.Meta[0] != '1' && msg.Meta[4] != byte(i) { t.Error("Shound emit requests in right order", string(msg.Meta)) } msg, _ = input.PluginRead() if msg.Meta[0] != '2' && msg.Meta[4] != byte(i) { t.Error("Shound emit responses in right order", string(msg.Meta)) } } os.Remove(file1.Name()) os.Remove(file2.Name()) } func TestInputFileLoop(t *testing.T) { rnd := rand.Int63() file, _ := os.OpenFile(fmt.Sprintf("/tmp/%d", rnd), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0660) file.Write([]byte("1 1 1\ntest1")) file.Write([]byte(payloadSeparator)) file.Write([]byte("1 1 2\ntest2")) file.Write([]byte(payloadSeparator)) file.Close() input := NewFileInput(fmt.Sprintf("/tmp/%d", rnd), true, 100, 0, false) // Even if we have just 2 requests in file, it should indifinitly loop for i := 0; i < 1000; i++ { input.PluginRead() } input.Close() os.Remove(file.Name()) } func TestInputFileCompressed(t *testing.T) { rnd := rand.Int63() output := NewFileOutput(fmt.Sprintf("/tmp/%d_0.gz", rnd), &FileOutputConfig{FlushInterval: time.Minute, Append: true}) for i := 0; i < 1000; i++ { output.PluginWrite(&Message{Meta: []byte("1 1 1\r\n"), Data: []byte("test")}) } name1 := output.file.Name() output.Close() output2 := NewFileOutput(fmt.Sprintf("/tmp/%d_1.gz", rnd), &FileOutputConfig{FlushInterval: time.Minute, Append: true}) for i := 0; i < 1000; i++ { output2.PluginWrite(&Message{Meta: []byte("1 1 1\r\n"), Data: []byte("test")}) } name2 := output2.file.Name() output2.Close() input := NewFileInput(fmt.Sprintf("/tmp/%d*", rnd), false, 100, 0, false) for i := 0; i < 2000; i++ { input.PluginRead() } os.Remove(name1) os.Remove(name2) } type CaptureFile struct { msgs []*Message file *os.File } func NewExpectedCaptureFile(msgs []*Message, file *os.File) *CaptureFile { ecf := new(CaptureFile) ecf.file = file ecf.msgs = msgs return ecf } func (expectedCaptureFile *CaptureFile) TearDown() { if expectedCaptureFile.file != nil { os.Remove(expectedCaptureFile.file.Name()) } } type RequestGenerator struct { inputs []PluginReader emit func() wg *sync.WaitGroup } func NewRequestGenerator(inputs []PluginReader, emit func(), count int) (rg *RequestGenerator) { rg = new(RequestGenerator) rg.inputs = inputs rg.emit = emit rg.wg = new(sync.WaitGroup) rg.wg.Add(count) return } func (expectedCaptureFile *CaptureFile) PayloadsEqual(other []*Message) bool { if len(expectedCaptureFile.msgs) != len(other) { return false } for i, payload := range other { if !bytes.Equal(expectedCaptureFile.msgs[i].Meta, payload.Meta) { return false } if !bytes.Equal(expectedCaptureFile.msgs[i].Data, payload.Data) { return false } } return true } func CreateCaptureFile(requestGenerator *RequestGenerator) *CaptureFile { f, err := ioutil.TempFile("", "testmainconf") if err != nil { panic(err) } readPayloads := []*Message{} output := NewTestOutput(func(msg *Message) { readPayloads = append(readPayloads, msg) requestGenerator.wg.Done() }) outputFile := NewFileOutput(f.Name(), &FileOutputConfig{FlushInterval: time.Second, Append: true}) plugins := &InOutPlugins{ Inputs: requestGenerator.inputs, Outputs: []PluginWriter{output, outputFile}, } for _, input := range requestGenerator.inputs { plugins.All = append(plugins.All, input) } plugins.All = append(plugins.All, output, outputFile) emitter := NewEmitter() go emitter.Start(plugins, Settings.Middleware) requestGenerator.emit() requestGenerator.wg.Wait() time.Sleep(100 * time.Millisecond) emitter.Close() return NewExpectedCaptureFile(readPayloads, f) } func ReadFromCaptureFile(captureFile *os.File, count int, callback writeCallback) (err error) { wg := new(sync.WaitGroup) input := NewFileInput(captureFile.Name(), false, 100, 0, false) output := NewTestOutput(func(msg *Message) { callback(msg) wg.Done() }) plugins := &InOutPlugins{ Inputs: []PluginReader{input}, Outputs: []PluginWriter{output}, } plugins.All = append(plugins.All, input, output) wg.Add(count) emitter := NewEmitter() go emitter.Start(plugins, Settings.Middleware) done := make(chan int, 1) go func() { wg.Wait() done <- 1 }() select { case <-done: break case <-time.After(2 * time.Second): err = errors.New("Timed out") } emitter.Close() return }