analysis_test.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. /*
  2. * Copyright (c) 2018, Psiphon Inc.
  3. * All rights reserved.
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. *
  18. */
  19. package analysis
  20. import (
  21. "reflect"
  22. "testing"
  23. )
  24. func TestLogLinesWithExpectations(t *testing.T) {
  25. var l *LogStats
  26. t.Run("test log model parsing and sorting", func(t *testing.T) {
  27. l = parseLogsAndTestExpectations(logLinesWithExpectations(), t)
  28. logs, _ := l.SortLogModels(true, true, true)
  29. var prevLogCount uint
  30. for _, x := range logs {
  31. var count uint
  32. switch v := x.(type) {
  33. case MessageLogModelStats:
  34. count = v.Count
  35. case MetricsLogModelStats:
  36. count = v.Count
  37. case UnknownLogModelStats:
  38. count = v.Count
  39. default:
  40. t.Errorf("Encountered unexpected struct of type %v\n", reflect.TypeOf(v))
  41. }
  42. if prevLogCount != 0 && prevLogCount > count {
  43. t.Errorf("Expected list to be sorted in ascending order")
  44. }
  45. prevLogCount = count
  46. }
  47. })
  48. }
  49. func TestMessageLogsWithErrorAndContext(t *testing.T) {
  50. logs := []LogLineWithExpectation{
  51. // The following messages should parse into 4 distinct log models
  52. messageLogExpectation(`{"msg":"a", "level":"info"}`),
  53. messageLogExpectation(`{"msg":"a", "level":"info"}`),
  54. messageLogExpectation(`{"msg":"a", "level":"info", "error": "b"}`),
  55. messageLogExpectation(`{"msg":"a", "level":"info", "error": "b"}`),
  56. messageLogExpectation(`{"msg":"a", "level":"info", "context": "c"}`),
  57. messageLogExpectation(`{"msg":"a", "level":"info", "context": "c"}`),
  58. messageLogExpectation(`{"msg":"a", "level":"info", "error": "b", "context": "c"}`),
  59. messageLogExpectation(`{"msg":"a", "level":"info", "error": "b", "context": "c"}`),
  60. // The following messages should parse into 2 distinct log models
  61. messageLogExpectation(`{"msg":"b", "level":"info", "error": "b"}`),
  62. messageLogExpectation(`{"msg":"b", "level":"info", "context": "b"}`),
  63. // The following messages should parse into 2 distinct log models
  64. messageLogExpectation(`{"msg":"c", "level":"info"}`),
  65. messageLogExpectation(`{"msg":"c", "level":"warning"}`),
  66. }
  67. l := parseLogsAndTestExpectations(logs, t)
  68. numLogModels := len(l.MessageLogModels.modelStats)
  69. expectedUniqueModels := 8
  70. if numLogModels != expectedUniqueModels {
  71. t.Errorf("Expected %d message log models but found %d\n", expectedUniqueModels, numLogModels)
  72. }
  73. }
  74. // Helpers
  75. type LogLineWithExpectation struct {
  76. log string
  77. expects parseLineExpectation
  78. }
  79. type parseLineExpectation struct {
  80. error bool
  81. message bool
  82. metrics bool
  83. unknown bool
  84. }
  85. func parseLogsAndTestExpectations(expectations []LogLineWithExpectation, t *testing.T) (l *LogStats) {
  86. l = NewLogStats()
  87. for _, expectation := range expectations {
  88. snapshot := *l
  89. err := l.ParseLogLine(expectation.log)
  90. // Check that the expectation is valid
  91. if !(expectation.expects.error || expectation.expects.message ||
  92. expectation.expects.metrics || expectation.expects.unknown) {
  93. t.Errorf("Malformed expectation expects nothing")
  94. t.FailNow()
  95. }
  96. // Check error expectation
  97. if err != nil {
  98. if expectation.expects.error != true {
  99. t.Errorf("Unexpected error: < %s >, from log line: \"%s\"\n", err, expectation.log)
  100. }
  101. }
  102. // Check message expectation
  103. if expectation.expects.message {
  104. if l.MessageLogModels.Count != snapshot.MessageLogModels.Count+1 {
  105. t.Errorf("Expected message log from: \"%s\"\n", expectation.log)
  106. }
  107. }
  108. // Check metric expectation
  109. if expectation.expects.metrics {
  110. if l.MetricsLogModels.Count != snapshot.MetricsLogModels.Count+1 {
  111. t.Errorf("Expected metric log model from: \"%s\"\n", expectation.log)
  112. }
  113. }
  114. // Check unknown expectation
  115. if expectation.expects.unknown {
  116. if l.UnknownLogModels.Count != snapshot.UnknownLogModels.Count+1 {
  117. t.Errorf("Expected unknown log model from: \"%s\"\n", expectation.log)
  118. }
  119. }
  120. }
  121. return l
  122. }
  123. func logLinesWithExpectations() (l []LogLineWithExpectation) {
  124. l = []LogLineWithExpectation{
  125. // ************
  126. // Message logs
  127. // ************
  128. // Test collision of basic message logs
  129. messageLogExpectation(`{"msg":"a", "level":"info"}`),
  130. messageLogExpectation(`{"msg":"a", "level":"info"}`),
  131. // Different valid levels
  132. messageLogExpectation(`{"msg":"a", "level":"debug"}`),
  133. messageLogExpectation(`{"msg":"a", "level":"warning"}`),
  134. messageLogExpectation(`{"msg":"a", "level":"error"}`),
  135. // ************
  136. // Metrics logs
  137. // ************
  138. // Test collision of basic metrics logs
  139. metricsLogExpectation(`{"event_name":"a"}`),
  140. metricsLogExpectation(`{"event_name":"a"}`),
  141. // ************
  142. // Unknown logs
  143. // ************
  144. unknownLogExpectation(`{}`),
  145. unknownLogExpectation(`{"a":"b"}`),
  146. // Test collision of unknown logs with depth
  147. unknownLogExpectation(`{"a":{"b":[{"c":{}}]}}`),
  148. unknownLogExpectation(`{"a":{"b":[{"c":{}}]}}`),
  149. // Message log line missing level field
  150. unknownLogExpectation(`{"msg":"a"}`),
  151. // **************
  152. // Malformed logs
  153. // **************
  154. malformedLogExpectation(`{`),
  155. // Invalid message log levels
  156. malformedLogExpectation(`{"msg":"a", "level":"{"}`),
  157. malformedLogExpectation(`{"msg":"a", "level":"unknown"}`),
  158. }
  159. return l
  160. }
  161. func messageLogExpectation(log string) LogLineWithExpectation {
  162. return LogLineWithExpectation{
  163. log: log,
  164. expects: parseLineExpectation{
  165. message: true,
  166. },
  167. }
  168. }
  169. func metricsLogExpectation(log string) LogLineWithExpectation {
  170. return LogLineWithExpectation{
  171. log: log,
  172. expects: parseLineExpectation{
  173. metrics: true,
  174. },
  175. }
  176. }
  177. func unknownLogExpectation(log string) LogLineWithExpectation {
  178. return LogLineWithExpectation{
  179. log: log,
  180. expects: parseLineExpectation{
  181. unknown: true,
  182. },
  183. }
  184. }
  185. func malformedLogExpectation(log string) LogLineWithExpectation {
  186. return LogLineWithExpectation{
  187. log: log,
  188. expects: parseLineExpectation{
  189. error: true,
  190. },
  191. }
  192. }