github.com/magnusbaeck/logstash-filter-verifier/v2@v2.0.0-pre.1/logstash/parallel_process_test.go (about) 1 // Copyright (c) 2016-2018 Magnus Bäck <magnus@noun.se> 2 3 package logstash 4 5 import ( 6 "errors" 7 "fmt" 8 "io/ioutil" 9 "os" 10 "reflect" 11 "testing" 12 "time" 13 14 semver "github.com/Masterminds/semver/v3" 15 ) 16 17 func TestParallelProcess(t *testing.T) { 18 const testLine = "test line\n" 19 20 fs := FieldSet{} 21 ts, err := NewTestStream("Codec", fs, 5*time.Second) 22 if err != nil { 23 t.Fatalf("Unable to create TestStream: %s", err) 24 } 25 defer CleanupTestStreams([]*TestStream{ts}) 26 27 file, err := ioutil.TempFile("", "") 28 if err != nil { 29 t.Fatalf("Failed to create temporary config file: %s", err) 30 } 31 configPaths := []string{file.Name()} 32 33 // Pretend it's an old Logstash; if NewInvocation() is called 34 // for 5.0 or newer it'll try to copy configuration files so 35 // we'd have to generate such files too. 36 v, err := semver.NewVersion("2.4.0") 37 if err != nil { 38 t.Fatalf("Unable to parse version number: %s", err) 39 } 40 41 inv, err := NewInvocation(os.Args[0], []string{}, v, configPaths...) 42 if err != nil { 43 t.Fatalf("Unable to create Invocation: %s", err) 44 } 45 defer inv.Release() 46 p, err := NewParallelProcess(inv, []*TestStream{ts}, []string{}) 47 if err != nil { 48 t.Fatalf("Unable to create ParallelProcess: %s", err) 49 } 50 defer p.Release() 51 52 p.child.Env = append(os.Environ(), "TEST_MAIN=logstash-mock", "TEST_SOCKET="+ts.senderPath) 53 54 if err = p.Start(); err != nil { 55 t.Fatalf("Unable to start ParallelProcess: %s", err) 56 } 57 58 _, err = ts.Write([]byte(testLine)) 59 if err != nil { 60 t.Fatalf("Unable to write to TestStream: %s", err) 61 } 62 if err = ts.Close(); err != nil { 63 t.Fatalf("Unable to close TestStream: %s", err) 64 } 65 66 result, err := p.Wait() 67 if err != nil { 68 t.Fatalf("Error while waiting for ParallelProcess to finish: %s", err) 69 } 70 if result.Output != testLine { 71 t.Errorf("Unexpected return from ParallelProcess, expected: %s, got: %s", testLine, result.Output) 72 } 73 } 74 75 func TestGetSocketInOutPlugins(t *testing.T) { 76 // Create a single temporary file that all test cases can use. 77 receiver, err := newDeletedTempFile("", "") 78 if err != nil { 79 t.Fatalf("Unable to create temporary file: %s", err) 80 } 81 defer receiver.Close() 82 83 cases := []struct { 84 streams []*TestStream 85 expectedInputs []string 86 expectedOutputs []string 87 err error 88 }{ 89 // Single TestStream struct. 90 { 91 []*TestStream{ 92 { 93 senderPath: "/tmp/foo", 94 inputCodec: "any_codec", 95 fields: FieldSet{}, 96 receiver: receiver, 97 }, 98 }, 99 []string{ 100 "unix { mode => \"client\" path => \"/tmp/foo\" codec => any_codec " + 101 "add_field => { \"[@metadata][__lfv_testcase]\" => \"0\" } }", 102 }, 103 []string{ 104 fmt.Sprintf("if [@metadata][__lfv_testcase] == \"0\" { file { path => %q codec => \"json_lines\" } }", receiver.Name()), 105 }, 106 nil, 107 }, 108 // Multiple TestStream structs. 109 { 110 []*TestStream{ 111 { 112 senderPath: "/tmp/foo", 113 inputCodec: "any_codec", 114 fields: FieldSet{}, 115 receiver: receiver, 116 }, 117 { 118 senderPath: "/tmp/bar", 119 inputCodec: "other_codec", 120 fields: FieldSet{}, 121 receiver: receiver, 122 }, 123 }, 124 []string{ 125 "unix { mode => \"client\" path => \"/tmp/foo\" codec => any_codec " + 126 "add_field => { \"[@metadata][__lfv_testcase]\" => \"0\" } }", 127 "unix { mode => \"client\" path => \"/tmp/bar\" codec => other_codec " + 128 "add_field => { \"[@metadata][__lfv_testcase]\" => \"1\" } }", 129 }, 130 []string{ 131 fmt.Sprintf("if [@metadata][__lfv_testcase] == \"0\" { file { path => %q codec => \"json_lines\" } }", receiver.Name()), 132 fmt.Sprintf("if [@metadata][__lfv_testcase] == \"1\" { file { path => %q codec => \"json_lines\" } }", receiver.Name()), 133 }, 134 nil, 135 }, 136 // Single TestStream struct with additional fields set. 137 { 138 []*TestStream{ 139 { 140 senderPath: "/tmp/foo", 141 inputCodec: "any_codec", 142 fields: FieldSet{ 143 "@metadata": map[string]interface{}{ 144 "foo": "bar", 145 }, 146 }, 147 receiver: receiver, 148 }, 149 }, 150 []string{ 151 "unix { mode => \"client\" path => \"/tmp/foo\" codec => any_codec " + 152 "add_field => { \"[@metadata][__lfv_testcase]\" => \"0\" \"[@metadata][foo]\" => \"bar\" } }", 153 }, 154 []string{ 155 fmt.Sprintf("if [@metadata][__lfv_testcase] == \"0\" { file { path => %q codec => \"json_lines\" } }", receiver.Name()), 156 }, 157 nil, 158 }, 159 // Single TestStream struct with a non-map @metadata 160 // field should result in an error. 161 { 162 []*TestStream{ 163 { 164 senderPath: "/tmp/foo", 165 inputCodec: "any_codec", 166 fields: FieldSet{ 167 "@metadata": "foo", 168 }, 169 receiver: receiver, 170 }, 171 }, 172 nil, 173 nil, 174 errors.New("the supplied contents of the @metadata field must be a hash (found string instead)"), 175 }, 176 } 177 for i, c := range cases { 178 inputs, outputs, err := getSocketInOutPlugins(c.streams) 179 180 if err == nil && c.err != nil { 181 t.Errorf("Test %d: Expected failure, got success.", i) 182 } else if err != nil && c.err == nil { 183 t.Errorf("Test %d: Expected success, got this error instead: %#v", i, err) 184 } else if err != nil && c.err != nil && err.Error() != c.err.Error() { 185 t.Errorf("Test %d: Didn't get the expected error.\nExpected:\n%s\nGot:\n%s", i, c.err, err) 186 } else { 187 if !reflect.DeepEqual(c.expectedInputs, inputs) { 188 t.Errorf("Test %d:\nExpected:\n%#v\nGot:\n%#v", i, c.expectedInputs, inputs) 189 } 190 if !reflect.DeepEqual(c.expectedOutputs, outputs) { 191 t.Errorf("Test %d:\nExpected:\n%#v\nGot:\n%#v", i, c.expectedOutputs, outputs) 192 } 193 } 194 } 195 }