parsing_test.go 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. package parser
  2. import (
  3. "bytes"
  4. "fmt"
  5. "html/template"
  6. "io"
  7. "io/ioutil"
  8. "os"
  9. "testing"
  10. "github.com/crowdsecurity/crowdsec/pkg/types"
  11. "github.com/davecgh/go-spew/spew"
  12. log "github.com/sirupsen/logrus"
  13. "gopkg.in/yaml.v2"
  14. )
  15. type TestFile struct {
  16. Lines []types.Event `yaml:"lines,omitempty"`
  17. Results []types.Event `yaml:"results,omitempty"`
  18. }
  19. func TestParser(t *testing.T) {
  20. var envSetting = os.Getenv("TEST_ONLY")
  21. if envSetting != "" {
  22. if err := testOneParser(t, envSetting); err != nil {
  23. t.Fatalf("Test '%s' failed : %s", envSetting, err)
  24. }
  25. } else {
  26. fds, err := ioutil.ReadDir("./tests/")
  27. if err != nil {
  28. t.Fatalf("Unable to read test directory : %s", err)
  29. }
  30. for _, fd := range fds {
  31. fname := "./tests/" + fd.Name()
  32. log.Infof("Running test on %s", fname)
  33. if err := testOneParser(t, fname); err != nil {
  34. t.Fatalf("Test '%s' failed : %s", fname, err)
  35. }
  36. }
  37. }
  38. }
  39. func testOneParser(t *testing.T, dir string) error {
  40. var p UnixParser
  41. var pctx *UnixParserCtx
  42. var err error
  43. var pnodes []Node
  44. log.SetLevel(log.DebugLevel)
  45. datadir := "../../data/"
  46. cfgdir := "../../config/"
  47. /* this should be refactored to 2 lines :p */
  48. // Init the parser
  49. pctx, err = p.Init(map[string]interface{}{"patterns": cfgdir + string("/patterns/")})
  50. if err != nil {
  51. return fmt.Errorf("failed to initialize parser : %v", err)
  52. }
  53. //Init the enricher
  54. pplugins, err := Loadplugin(datadir)
  55. if err != nil {
  56. return fmt.Errorf("failed to load plugin geoip : %v", err)
  57. }
  58. ECTX = append(ECTX, pplugins)
  59. log.Debugf("Geoip ctx : %v", ECTX)
  60. //Load the parser configuration
  61. var parser_configs []Stagefile
  62. //TBD var po_parser_configs []Stagefile
  63. parser_cfg_file := fmt.Sprintf("%s/parsers.yaml", dir)
  64. b, err := ioutil.ReadFile(parser_cfg_file)
  65. if err != nil {
  66. return fmt.Errorf("failed opening %s : %s", parser_cfg_file, err)
  67. }
  68. tmpl, err := template.New("test").Parse(string(b))
  69. if err != nil {
  70. return fmt.Errorf("failed to parse template %s : %s", b, err)
  71. }
  72. var out bytes.Buffer
  73. err = tmpl.Execute(&out, map[string]string{"TestDirectory": dir})
  74. if err != nil {
  75. panic(err)
  76. }
  77. if err := yaml.UnmarshalStrict(out.Bytes(), &parser_configs); err != nil {
  78. return fmt.Errorf("failed unmarshaling %s : %s", parser_cfg_file, err)
  79. }
  80. pnodes, err = LoadStages(parser_configs, pctx)
  81. if err != nil {
  82. return fmt.Errorf("unable to load parser config : %s", err)
  83. }
  84. //TBD: Load post overflows
  85. //func testFile(t *testing.T, file string, pctx UnixParserCtx, nodes []Node) bool {
  86. parser_test_file := fmt.Sprintf("%s/test.yaml", dir)
  87. if testFile(t, parser_test_file, *pctx, pnodes) != true {
  88. return fmt.Errorf("test failed !")
  89. }
  90. return nil
  91. }
  92. func testFile(t *testing.T, file string, pctx UnixParserCtx, nodes []Node) bool {
  93. var expects []types.Event
  94. /* now we can load the test files */
  95. //process the yaml
  96. yamlFile, err := os.Open(file)
  97. if err != nil {
  98. t.Errorf("yamlFile.Get err #%v ", err)
  99. }
  100. dec := yaml.NewDecoder(yamlFile)
  101. dec.SetStrict(true)
  102. for {
  103. tf := TestFile{}
  104. err := dec.Decode(&tf)
  105. if err != nil {
  106. if err == io.EOF {
  107. log.Warningf("end of test file")
  108. break
  109. }
  110. t.Errorf("Failed to load testfile '%s' yaml error : %v", file, err)
  111. return false
  112. }
  113. for _, in := range tf.Lines {
  114. log.Debugf("Parser input : %s", spew.Sdump(in))
  115. out, err := Parse(pctx, in, nodes)
  116. if err != nil {
  117. log.Errorf("Failed to process %s : %v", spew.Sdump(in), err)
  118. }
  119. log.Debugf("Parser output : %s", spew.Sdump(out))
  120. expects = append(expects, out)
  121. }
  122. /*
  123. check the results we got against the expected ones
  124. only the keys of the expected part are checked against result
  125. */
  126. if len(tf.Results) == 0 && len(expects) == 0 {
  127. t.Errorf("No results, no tests, abort.")
  128. return false
  129. //return false
  130. }
  131. redo:
  132. if len(tf.Results) == 0 && len(expects) == 0 {
  133. log.Warningf("Test is successfull")
  134. return true
  135. } else {
  136. log.Warningf("%d results to check against %d expected results", len(expects), len(tf.Results))
  137. }
  138. for eidx, out := range expects {
  139. for ridx, expected := range tf.Results {
  140. log.Debugf("Checking next expected result.")
  141. valid := true
  142. //allow to check as well for stage and processed flags
  143. if expected.Stage != "" {
  144. if expected.Stage != out.Stage {
  145. log.Infof("out/expected mismatch 'Stage' value : (got) '%s' != (expected) '%s'", out.Stage, expected.Stage)
  146. valid = false
  147. goto CheckFailed
  148. } else {
  149. log.Infof("Stage == '%s'", expected.Stage)
  150. }
  151. }
  152. if expected.Process != out.Process {
  153. log.Infof("out/expected mismatch 'Process' value : (got) '%t' != (expected) '%t'", out.Process, expected.Process)
  154. valid = false
  155. goto CheckFailed
  156. } else {
  157. log.Infof("Process == '%t'", out.Process)
  158. }
  159. if expected.Whitelisted != out.Whitelisted {
  160. log.Infof("out/expected mismatch 'Whitelisted' value : (got) '%t' != (expected) '%t'", out.Whitelisted, expected.Whitelisted)
  161. valid = false
  162. goto CheckFailed
  163. } else {
  164. log.Infof("Whitelisted == '%t'", out.Whitelisted)
  165. }
  166. for k, v := range expected.Parsed {
  167. /*check 3 main dicts : event, enriched, meta */
  168. if val, ok := out.Parsed[k]; ok {
  169. if val != v {
  170. log.Infof("out/expected mismatch 'event' entry [%s] : (got) '%s' != (expected) '%s'", k, val, v)
  171. valid = false
  172. goto CheckFailed
  173. } else {
  174. log.Infof(".Parsed[%s] == '%s'", k, val)
  175. }
  176. } else {
  177. log.Infof("missing event entry [%s] in expected : %v", k, out.Parsed)
  178. valid = false
  179. goto CheckFailed
  180. }
  181. }
  182. for k, v := range expected.Meta {
  183. /*check 3 main dicts : event, enriched, meta */
  184. if val, ok := out.Meta[k]; ok {
  185. if val != v {
  186. log.Infof("out/expected mismatch 'meta' entry [%s] : (got) '%s' != (expected) '%s'", k, val, v)
  187. valid = false
  188. goto CheckFailed
  189. } else {
  190. log.Infof("Meta[%s] == '%s'", k, val)
  191. }
  192. } else {
  193. log.Warningf("missing meta entry [%s] in expected", k)
  194. valid = false
  195. goto CheckFailed
  196. }
  197. }
  198. for k, v := range expected.Enriched {
  199. /*check 3 main dicts : event, enriched, meta */
  200. if val, ok := out.Enriched[k]; ok {
  201. if val != v {
  202. log.Infof("out/expected mismatch 'Enriched' entry [%s] : (got) '%s' != (expected) '%s'", k, val, v)
  203. valid = false
  204. goto CheckFailed
  205. } else {
  206. log.Infof("Enriched[%s] == '%s'", k, val)
  207. }
  208. } else {
  209. log.Warningf("missing enriched entry [%s] in expected", k)
  210. valid = false
  211. goto CheckFailed
  212. }
  213. }
  214. CheckFailed:
  215. if valid {
  216. //log.Infof("Found result [%s], skip", spew.Sdump(tf.Results[ridx]))
  217. log.Warningf("The test is valid, remove entry %d from expects, and %d from t.Results", eidx, ridx)
  218. //don't do this at home : delete current element from list and redo
  219. expects[eidx] = expects[len(expects)-1]
  220. expects = expects[:len(expects)-1]
  221. tf.Results[ridx] = tf.Results[len(tf.Results)-1]
  222. tf.Results = tf.Results[:len(tf.Results)-1]
  223. goto redo
  224. }
  225. }
  226. }
  227. }
  228. t.Errorf("failed test")
  229. return false
  230. }