github.com/psiphon-Labs/psiphon-tunnel-core@v2.0.28+incompatible/Server/logging/analysis/analysis_test.go (about) 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 20 package analysis 21 22 import ( 23 "reflect" 24 "testing" 25 ) 26 27 func TestAllLogModelsAndSorting(t *testing.T) { 28 l := parseLogsAndTestExpectations(logLinesWithExpectations(), t) 29 30 logs, _ := l.SortLogModels(true, true, true) 31 var prevLogCount uint 32 33 for _, x := range logs { 34 var count uint 35 36 switch v := x.(type) { 37 case MessageLogModelStats: 38 count = v.Count 39 case MetricsLogModelStats: 40 count = v.Count 41 case UnknownLogModelStats: 42 count = v.Count 43 default: 44 t.Errorf("Encountered unexpected struct of type %v\n", reflect.TypeOf(v)) 45 } 46 47 if prevLogCount != 0 && prevLogCount > count { 48 t.Errorf("Expected list to be sorted in ascending order") 49 } 50 prevLogCount = count 51 } 52 } 53 54 func TestMessageLogsWithErrorAndContext(t *testing.T) { 55 logs := []LogLineWithExpectation{ 56 // The following messages should parse into 4 distinct log models 57 messageLogExpectation(`{"msg":"a", "level":"info"}`), 58 messageLogExpectation(`{"msg":"a", "level":"info"}`), 59 messageLogExpectation(`{"msg":"a", "level":"info", "error": "b"}`), 60 messageLogExpectation(`{"msg":"a", "level":"info", "error": "b"}`), 61 messageLogExpectation(`{"msg":"a", "level":"info", "context": "c"}`), 62 messageLogExpectation(`{"msg":"a", "level":"info", "context": "c"}`), 63 messageLogExpectation(`{"msg":"a", "level":"info", "error": "b", "context": "c"}`), 64 messageLogExpectation(`{"msg":"a", "level":"info", "error": "b", "context": "c"}`), 65 66 // The following messages should parse into 2 distinct log models 67 messageLogExpectation(`{"msg":"b", "level":"info", "error": "b"}`), 68 messageLogExpectation(`{"msg":"b", "level":"info", "context": "b"}`), 69 70 // The following messages should parse into 2 distinct log models 71 messageLogExpectation(`{"msg":"c", "level":"info"}`), 72 messageLogExpectation(`{"msg":"c", "level":"warning"}`), 73 } 74 75 l := parseLogsAndTestExpectations(logs, t) 76 77 numLogModels := len(l.MessageLogModels.modelStats) 78 expectedUniqueModels := 8 79 if numLogModels != expectedUniqueModels { 80 t.Errorf("Expected %d message log models but found %d\n", expectedUniqueModels, numLogModels) 81 } 82 } 83 84 func TestMessageLogsWithRedactedIpAddresses(t *testing.T) { 85 logs := []LogLineWithExpectation{ 86 // The following messages should parse into 1 distinct log model 87 messageLogExpectation(`{"msg":"a", "level":"info", "error": "1.1.1.1->2.2.2.2"}`), 88 messageLogExpectation(`{"msg":"a", "level":"info", "error": "3.3.3.3->4.4.4.4"}`), 89 messageLogExpectation(`{"msg":"a", "level":"info", "error": "1.1.1.1->2.2.2.2:1"}`), 90 messageLogExpectation(`{"msg":"a", "level":"info", "error": "1.1.1.1->2.2.2.2:65535"}`), 91 } 92 93 l := parseLogsAndTestExpectations(logs, t) 94 95 numLogModels := len(l.MessageLogModels.modelStats) 96 expectedUniqueModels := 1 97 if numLogModels != expectedUniqueModels { 98 t.Errorf("Expected %d message log models but found %d\n", expectedUniqueModels, numLogModels) 99 } 100 } 101 102 // Helpers 103 104 type LogLineWithExpectation struct { 105 log string 106 expects parseLineExpectation 107 } 108 109 type parseLineExpectation struct { 110 error bool 111 message bool 112 metrics bool 113 unknown bool 114 } 115 116 func parseLogsAndTestExpectations(expectations []LogLineWithExpectation, t *testing.T) (l *LogStats) { 117 l = NewLogStats() 118 119 for _, expectation := range expectations { 120 snapshot := *l 121 122 err := l.ParseLogLine(expectation.log) 123 124 // Check that the expectation is valid 125 if !(expectation.expects.error || expectation.expects.message || 126 expectation.expects.metrics || expectation.expects.unknown) { 127 t.Errorf("Malformed expectation expects nothing") 128 t.FailNow() 129 } 130 131 // Check error expectation 132 if err != nil { 133 if expectation.expects.error != true { 134 t.Errorf("Unexpected error: < %s >, from log line: \"%s\"\n", err, expectation.log) 135 } 136 } 137 138 // Check message expectation 139 if expectation.expects.message { 140 if l.MessageLogModels.Count != snapshot.MessageLogModels.Count+1 { 141 t.Errorf("Expected message log from: \"%s\"\n", expectation.log) 142 } 143 } 144 145 // Check metric expectation 146 if expectation.expects.metrics { 147 if l.MetricsLogModels.Count != snapshot.MetricsLogModels.Count+1 { 148 t.Errorf("Expected metric log model from: \"%s\"\n", expectation.log) 149 } 150 } 151 152 // Check unknown expectation 153 if expectation.expects.unknown { 154 if l.UnknownLogModels.Count != snapshot.UnknownLogModels.Count+1 { 155 t.Errorf("Expected unknown log model from: \"%s\"\n", expectation.log) 156 } 157 } 158 } 159 160 return l 161 } 162 163 func logLinesWithExpectations() (l []LogLineWithExpectation) { 164 l = []LogLineWithExpectation{ 165 166 // ************ 167 // Message logs 168 // ************ 169 170 // Test collision of basic message logs 171 messageLogExpectation(`{"msg":"a", "level":"info"}`), 172 messageLogExpectation(`{"msg":"a", "level":"info"}`), 173 174 // Different valid levels 175 messageLogExpectation(`{"msg":"a", "level":"debug"}`), 176 messageLogExpectation(`{"msg":"a", "level":"warning"}`), 177 messageLogExpectation(`{"msg":"a", "level":"error"}`), 178 179 // ************ 180 // Metrics logs 181 // ************ 182 183 // Test collision of basic metrics logs 184 metricsLogExpectation(`{"event_name":"a"}`), 185 metricsLogExpectation(`{"event_name":"a"}`), 186 187 // ************ 188 // Unknown logs 189 // ************ 190 191 unknownLogExpectation(`{}`), 192 unknownLogExpectation(`{"a":"b"}`), 193 194 // Test collision of unknown logs with depth 195 unknownLogExpectation(`{"a":{"b":[{"c":{}}]}}`), 196 unknownLogExpectation(`{"a":{"b":[{"c":{}}]}}`), 197 198 // Message log line missing level field 199 unknownLogExpectation(`{"msg":"a"}`), 200 201 // ************** 202 // Malformed logs 203 // ************** 204 205 malformedLogExpectation(`{`), 206 // Invalid message log levels 207 malformedLogExpectation(`{"msg":"a", "level":"{"}`), 208 malformedLogExpectation(`{"msg":"a", "level":"unknown"}`), 209 } 210 211 return l 212 } 213 214 func messageLogExpectation(log string) LogLineWithExpectation { 215 return LogLineWithExpectation{ 216 log: log, 217 expects: parseLineExpectation{ 218 message: true, 219 }, 220 } 221 } 222 223 func metricsLogExpectation(log string) LogLineWithExpectation { 224 return LogLineWithExpectation{ 225 log: log, 226 expects: parseLineExpectation{ 227 metrics: true, 228 }, 229 } 230 } 231 232 func unknownLogExpectation(log string) LogLineWithExpectation { 233 return LogLineWithExpectation{ 234 log: log, 235 expects: parseLineExpectation{ 236 unknown: true, 237 }, 238 } 239 } 240 241 func malformedLogExpectation(log string) LogLineWithExpectation { 242 return LogLineWithExpectation{ 243 log: log, 244 expects: parseLineExpectation{ 245 error: true, 246 }, 247 } 248 }